bzt wrote:In a nutshell: I'm saying if dynamic linker is in the kernel, then it can be more secure, as it can enforce WNX and deny writing code from user level. Some architecture supports this in hardware (like ARM), while others needs carefully assigned page attributes (like x86). Nullplan seems do disagree, however it looks more like he doesn't get the concept in the first place.
You don't need to put the dynamic linker into the kernel to enforce W^X. If you want to go farther in your hardening, you can require that all executable pages be mapped read-only from files the user cannot write (except root, obviously), and it still does not require you to put the dynlinker into the kernel.
And that does already harden as much as possible against stack smash classic (the original 1996 paper "Smashing the stack for fun and profit"), but absolutely fails to do anything against ROP. ASLR and stack protection mitigate, but the only thing that eliminates the problem is actually patching the fault.
bzt wrote:In a monolithic kernel everything is in the kernel. Why not this one?
No, only that which
must be in the kernel, is in the kernel. My kernel is monolithic, which means device drivers are also in the kernel, but, for instance, userspace programs are not. My kernel is lacking features others have, mostly for lack of will on my part, but sometimes out of conscious decision not to have it running in ring 0. I have support for frame buffers, and support for event-driven input devices of all kinds, and for pseudo-terminals, but not for virtual terminals (like Linux has). This means, I have no font-rendering in the kernel itself, no frame buffer drawing routines, nothing to map scan codes to glyphs (and that can be quite an astonishingly complex task in some languages), etc. So much stuff that is just not part of my kernel. Now, it does still exist. A frame buffer terminal application tying those device drivers together does exist, but it is entirely in userspace. If (for example) the font renderer passes out because a Hiragana character was combined with Devanagari diacritics, it will be a userspace program that dies, not the whole OS.
In that spirit, the dynamic linker is also not part of the kernel. Because it doesn't have to be.
bzt wrote:And about "parsing attacker-controlled data", isn't that true for everything? How about your kernel loading the interpreter? Or how about ACPI or AML? You don't support power management because that would mean parsing data in kernel?
Those things are unavoidably in the kernel. My kernel does the bare minimum of parsing of ELF headers to run the program, but what little it does is littered with boundary checks to ensure faulty data is detected and rejected, rather than leading to unpredictable program behavior, because in a kernel, that is undesirable. As for ACPI and AML, well, I use ACPICA for that (LAI was not a thing when I started that part, sorry Korona). But then, ACPi tables are not attacker-controlled. If the mainboard manufacturer wants to attack my OS, they have way better ways to get to it. Ways that I couldn't detect. Like using SMM, or going in through the embedded controller. Or just using Management Engine or whatever it is AMD calls the equivalent technology. But file system structures, ELF headers, dynamic headers, those are all potentially in the hands of unprivileged users, or hackers exploiting a running program. I know some programs are insecure, I don't have to hand them a privilege escalation to ring 0 on top of everything else.
bzt wrote:Yes, I did. Read the specification. Having either executable or writable pages makes buffer-overflows impossible. There's not much to explain, either you understand this, or you don't.
I am not interested in the minutiae of how ARM implements data execution prevention. I'll just believe you that it has provisions for it.
bzt wrote:With WNX buffer overflows might modify data, but they can't inject code.
And I keep saying ROP like a broken record. Modifying data is enough to get a program to misbehave in arbitrary ways. ROP is Turing-complete. You can do anything with it.
bzt wrote:Because that's obvious. If you put the dynamic linker in userspace, then you must allow to write code from userspace. First, you must load the code into memory (memory must be writable), second, you must modify the code with relocations. You can only do that if pages are writable that will be executed later.
A dynamic linker does not load files into memory by way of file I/O, but by using mmap(). It does not write code into memory, it maps code into memory. The difference is that the memory is never writable that way! It is write protected. While the code is loaded from disk (by way of page fault), it will be DMA'd into the physical memory. Unless the device somehow only supports PIO, but I haven't used that since the start of the millennium.
Second, the relocations do not modify the code. Which I wrote a hundred times already. Relocations, on anything from this century at least, only affect data.
bzt wrote:This sentence convinced me you don't have the required knowledge.
There's that arrogance again. You don't see me impugning your level of competence, do you?
bzt wrote:Yes, there are features that are more secure, like having an MMU and separated address spaces, or like denying writing executable pages. Are you really saying that an M68k without privilege levels of any kind is equally secure to an x86 with rings or ARM with ELs? Think it again.
Depends on what you mean. Are all platforms you named susceptible to attacks? Yes. Now, the effort required to attack an M68k system is way smaller than the effort to attack an x86 system, that is true. But is that enough? Hacking success as prize for hard work?
Is a lock that is not susceptible to most attacks, but absolutely breaks under one specific attack, any good? Any thief worth his salt will just use the one attack that breaks your lock. What would you think of a virus scanner that screens out 99% of all viruses, if the 1% it doesn't detect keeps wrecking your hard drive? "More secure" is a very seductive concept (we humans are very familiar with linear processes, after all), that is also completely invalid, because it does not prevent a determined attacker from breaking the system.
Security is not, when, in hindsight, you can say that you weren't successfully attacked. That might have just been luck. Security is about giving specific guarantees beforehand, about what can and, more importantly, cannot happen. And your little idea doesn't add anything useful to the list of security guarantees.
bzt wrote:You only wish. There are architectures which does not support PIC at all (x86 protected mode being one of them), and there are special instructions that always need relocation records (like "movabs" in x86_64, and short distance intermediates on ARM). A purely PIC instruction encoding would be nice, I agree, but sadly current processors are not like that.
What are you on about? x86, AMD64, and ARM all have mature ELF PIC ABIs, going back decades (well, not quite as far for AMD64, just one and a half decades). And yes, they do restrict code generation. For instance, movabs with external identifiers is not allowed in PIC. And the linker will tell you as much. And pretty much the only way to get them is to write assembly code yourself, badly, and then force the linker to emit textrels, anyway (because the default is to emit an error message.)
Is that what this is about? You wrote some bad assembly a while ago and now you think textrels are the norm? I am pleased to report that on my Linux system, there is not a single textrel anywhere.
bzt wrote:You are very, very very mistaken. Firefox is full of overflow problems which allow running arbitrary code (and this list is only for the last year). It even uses JIT compiled JavaScript, which by definition requires writeable *and* executable memory.
I did not choose Firefox as some kind of paragon of security, I chose it because it is a decently complicated piece of software (some would say overcomplicated), and even then, it does not have writable and executable memory mapped anywhere. Now, it is possible that it is switching some memory from writable to executable and back; my script would not catch that. My point was, that in the entire mess that is Firefox, there is not a single writable and executable mapping
as result of dynamic linking. What they do in the program itself is of no consequence to the argument.
bzt wrote:I beg to differ. Memory protection is per page. You can easily align GOT on page boundary,
I suspected as much. Actually I looked into something myself, and I found out that you just invented GNU RELRO.
I am curious, though: If you do have the dynlinker in the kernel, do you support lazy relocations? If so, how? If not, how do you deal with circular dependencies?