The point of a general-purpose computer is that it can do almost anything. It's an extremely flexible tool.
The point of a secure system is that it can't do most things. It can only do the things that are specified in the security requirements.
This is the inherent tension between security and flexibility (or usefulness or convenience or usability). General-purpose computers tend towards the flexibility end since that is the whole point. If you want security, buy a pocket calculator from a trustworthy vendor.
It is possible that the bridge can be crossed by dividing the system up into compartments, so that within a compartment there is full flexibility, but different compartments can't access each other. For example: processes, users, Docker containers or virtual machines.
But inevitably, people want different compartments to access each other. For a start, I want my mouse and keyboard and display screen to be able to interact with all compartments - otherwise why bother having them? But then a compartment running malicious code can trick me into thinking it's the login screen compartment so I type in my password. So instead we can say that the system always displays a compartment identifier at the top of the screen. Now we have increased security because it won't say "login screen" unless it's the login screen. But we have also decreased flexibility - what if I want to play a game in full-screen without this distracting title bar? Maybe we add a permission flag that says the title can be turned off for certain compartments. But then the video game may contain malware, so that after I leave and come back, it shows a fake login screen, including a fake login screen title. We decreased security again to gain flexibility. You see the tradeoff?
Secure Attention Key fits in this category. Only the system can respond to Ctrl-Alt-Del, so if you press Ctrl-Alt-Del and the system responds, you know your input was received by the system itself and not some program pretending to be the system. Remember the Windows XP login screen? But now, imagine you have a virtual machine and you want to log into the virtual machine, but you can't because the necessary key combination can only be received by the host system. (In practice, VMs from that era just overwrite Windows code to send the keypress to the VM. How does it know it's sending it to the VM and not some malware?)
It doesn't just apply to user interaction, but also network interaction, and disk interaction, for example. Perhaps we say that Word can only access files under /Files/Word and Firefox can only access files under /Files/Firefox. Cool... until you want to download a Word file and open it. Now you have to use the file manager (with access to all files) to move the file from /Files/Firefox to /Files/Word, before you can open it. High security, low convenience.
Or maybe we say Word also has access to all files the user double-clicks on that are associated with Word. Now you have three problems: (1) what if the user opens the file with File->Open instead of double-clicking, (2) what if the user opens the file by clicking in Firefox instead of in the file browser, (3) if Firefox can somehow trigger this behaviour when the user single-clicks in the download list, what prevents Word from sending the same trigger for files the user never clicked at all? Maybe we now have to enforce a standard file browser dialog for all file operations. Medium convenience, medium security. Low security if any application can just tell the system that the user clicked on any file. Low security if they can trick the user into clicking on files, such as by opening up a file browser dialog right before the user clicks.
XKCD sums up the situation well:
Systems often bake in these holes (since, again, it's their purpose) to the point where it's hard to create a solid boundary again. Both X11 and Win32, for example, contain big balls of compatibility mud that any process can call into, with not much security other than whether you can access it or not. And you can't even stop certain processes from accessing it - giving programs access to display stuff on the screen is the entire point of having a windowing system.
There's a compatibility ratchet mechanism here. Even when the system includes a technical mechanism to prevent access to something, applications come to rely on whatever access they have. In Windows 8 there is now ProcessSystemCallDisablePolicy which allows you to block access to the windowing system from specific processes. It's only narrowly applicable, because so much code relies on the available flexibility. Google tried to block it for Flash but Flash was using it to list the fonts installed on the system. A single call forces you to leave access enabled, or else people won't use your shiny new operating system that makes all their programs crash. Meanwhile, adding new access is easy.
In fact, it doesn't even always require a bug to break a security boundary, just an unexpected interaction. Windows lets one process send events (window messages) to another process's window. It also has a timer-expired event (WM_TIMER), one of whose arguments is a function pointer. See the problem? This was fine for Windows 3.1.
You can try to patch up holes that are too big, while being careful to allow legitimate calls. Windows's User Interface Privilege Isolation prevents medium-integrity processes from sending WM_TIMER messages (and all messages) to high-integrity processes. Such patches necessarily still leave many holes open, since they try to avoid blocking messages that actual software requires to work properly, so they are unlikely to guarantee security.