Separate Stack Segment in Protected mode?

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Separate Stack Segment in Protected mode?

Post by iansjack »

It seems a little inconsistent to say that the extra speed of long mode is “not important” and to then say that paging is worse than segmentation for performance considerations.
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Separate Stack Segment in Protected mode?

Post by rdos »

Schol-R-LEA wrote:We've gone over this many, many times before, rdos. Segmentation is not, and has never been, a protection mechanism, any more than paging is. While the protection mechanisms work together with them, it is not the protection mechanism, nor does it provide any more or any less protection than paging does.
I clearly claimed that I'm not discussing malicious code, rather the statistics that a bad pointer could corrupt vital kernel data or physical memory (in cases where all physical memory is mapped in the linear address space). In this regard, paging and paging + segmentation (yes, I'm not talking about segmentation on it's own) is better than running things with paging & segmentation turned off. This is obvious since every important system use paging and nothing runs without paging anymore. This is because if you run multiple processes without paging, then they can interfere with each other and take each other down. This is not acceptable. However, it seems like many people regard the same scenario in kernel space to be acceptable, which I find rather odd.
Schol-R-LEA wrote:
rdos wrote:The same scenario in long mode can lead to corruption of physical memory, vital kernel data, application data in another process, and even PCI BAR data.
That's simply not true, or rather, the claim that segmentation would prevent it is incorrect. Supervisor-mode pages have exactly the same degree of protection as supervisor-mode segments - a wild userland pointer to a supervisor data page is still going to be blocked by the protection mechanisms, because the page is marked as supervisor access only. A wild pointer in the kernel? True, that can access any virtual address currently mapped for the process, but the majority of addresses won't be mapped at all, meaning that a page fault will be caught by the memory manager, which presumably can determine that the page shouldn't be accessible and raise a protection fault. If it does hit an address that is live, then yes, a kernel bug can have the effect you describe - but the same is just as true with segmentation. A corrupted supervisor-mode pointer is a supervisor-mode pointer, period.
In the example I provided, long mode used a 64-bit linear pointer while the RDOS driver used a 32-bit based pointer. A 32-bit based pointer simply cannot access anything outside of the driver's own data segment, while a 64-bit linear pointer can access everything that is mapped in the linear address space, including vital kernel data and physical memory (if mapped).

I can elaborate further on this. API functions that drivers export typically use 64-bit linear pointers in long mode and 48-bit segmented pointers in RDOS. For long mode, the reasoning is similar: A 64-bit linear pointer can access everything, and so if corrupted, could sabotage a lot of stuff in kernel. For the 48-bit pointer in RDOS: If it comes from the application, it doesn't need to be validated since the segment is the flat user mode selector, which has a 3G limit. Thus, a user mode pointer can never corrupt kernel. If the pointer comes from another driver, then it is relative to some segment the driver manages, and so shouldn't be able to corrupt kernel. If a driver erranously uses a kernel selector, then corruption can occur. However, even if erranous kernel selectors are passed as pointers, the only possible harm that can be done is to corrupt the memory object itself. If the selector is a thread control block, then vital kernel data could become corrupted. Still, there is a lot more that needs to go wrong in the segmented model compared to the flat before vital kernel data is corrupted.

As for the argument that most addresses are not mapped and will cause page faults. That might be true for random addresses, but since most kernels are compiled into a compact module and so are data, it means that small changes to a pointer might be the difference between no adverse effects and a corrupt kernel. This is particularly true if the kernel use malloc from libc, as these implementations will typically allocate addresses close to each other.
Schol-R-LEA wrote:The only way what you are describing could work is if the driver segments are run in supervisor mode, but mapped separately from the kernel to their own code, stack, and data segments. As far as I am aware, this isn't possible - supervisor-mode memory will all have the same memory mapping, meaning that the kernel would have the same segmentation as the drivers. I can't see any way you can have separate segments within the supervisor memory space for the drivers distinct from the kernel itself - nor can I see how this differs from doing the same with paging, if so. As iansjack said, you can just as easily use separate page tables as you can separate segments.
Drivers are mapped to their own unique code & data segments. The kernel stack is mapped to a selector with a 4k limit, and every thread use their own kernel stack selector. When drivers use C, then the 32-bit compact memory model is used. It means that all references to static data will use 32-bit based pointers that always refer to the drivers own data segment. Malloc & free in the driver will create selectors of the allocated size and return 48-bit pointers. The GDT allocation mechanism is designed not to reuse selectors, rather will cycle through the complete GDT before starting from the start again. This means that if a memory object is freed, there is a need to allocate 1000s of new objects before it can be accessed again. This provides a good means to avoid using freed pointers, something that long mode cannot handle.

I do have a flat kernel mode memory selector that covers 4G, but I use it less and less. The major use today is to handle the disc cache & file systems. I'm phasing this out by moving the FS code to server processes and I will no longer map the cache in linear memory. The small memory allocator (kmalloc) never is used with flat addresses. The linear address is always assigned to a selector and return as selector:0. This means that the heap cannot easily be corrupted.
Schol-R-LEA wrote: I will again ask you a question you dodged previously: aside from x86, what other modern ISAs which support virtual memory (i.e., not a microcontroller) have you worked with? It is no coincidence that none of them use segmentation, because more or less all of them have had 32-bit or 64-bit memory addressing from the outset, and didn't need a hack to make a larger address space out of overlapping 16-bit memory addresses. Which is all that x86 segmentation was ever about. Yes, 32-bit protected mode went on to provide 32-bit segments, but that was only for backwards compatibility (the new mode had to have a GDT and LDTs which were supersets of those from 16-bit protected mode, otherwise, it couldn't run existing 16-bit protected mode code), not because they anticipated anyone using segmentation in 32-bit protected mode.
While the initial segmentation design of Intel certainly was a hack, the extension to 32-bits provided an excellent environment for writing code that discover more bugs than the flat memory models. So, I didn't start out with x86 because I like the hack, rather because I thought it was an elegant solution.
Schol-R-LEA wrote: What are you going to do if - or rather, when - Intel drops 32-bit protected mode, the same way they apparently plan to drop real mode? True, it probably won't be any time soon, but it is almost certainly coming - assuming that x86 remains the dominant desktop platform in the first place, which is increasingly unlikely with the growth of ARM platforms with comparable performance to the best x86-64 CPUs. What will you do if there are no more segmented platforms in common use?
That's easy. I will cease to work on OS development. No reason to write more poor Linux clones. :-)

As for ARM, I really dislike their development platform where you cannot fix buggy drivers or make hardware do things the developpers didn't anticipate. No way I would do ANYTHING on ARM unless I was paid for it.
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: Separate Stack Segment in Protected mode?

Post by Schol-R-LEA »

rdos wrote:As for ARM, I really dislike their development platform where you cannot fix buggy drivers or make hardware do things the developers didn't anticipate.
Wait, what? There is no one development platform for ARM; to the best of my knowledge, ARM Holdings themselves don't produce any hardware, they are only an IP holder, who license out their core designs to other manufacturers. Are you talking about ARM in general, or Broadcom SoCs (e.g., Raspberry Pi) specifically? Several ARM SoCs do not have the locked-down video drivers that one sees with the VideoCore GPUs - most use the Mali GPUs (using designs from ARM Holdings themselves), which while not open are well documented and have a FOSS driver codebase available for them. Similarly, there is a FOSS driver codebase for Qualcomm's Adreno GPUs.

Could you explain this statement? Am I missing something?
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Separate Stack Segment in Protected mode?

Post by rdos »

Schol-R-LEA wrote:
rdos wrote:As for ARM, I really dislike their development platform where you cannot fix buggy drivers or make hardware do things the developers didn't anticipate.
Wait, what? There is no one development platform for ARM; to the best of my knowledge, ARM Holdings themselves don't produce any hardware, they are only an IP holder, who license out their core designs to other manufacturers. Are you talking about ARM in general, or Broadcom SoCs (e.g., Raspberry Pi) specifically? Several ARM SoCs do not have the locked-down video drivers that one sees with the VideoCore GPUs - most use the Mali GPUs (using designs from ARM Holdings themselves), which while not open are well documented and have a FOSS driver codebase available for them. Similarly, there is a FOSS driver codebase for Qualcomm's Adreno GPUs.

Could you explain this statement? Am I missing something?
We used Keil for ARM development, the "IPs" seems to be locked down and lacking in source as well as a possibility to the modify code. It's possible there are other more "open" environments, but what you mention about IPs seems to indicate this is the policy of ARM rather than of those that provide Keil. I don't think this is acceptable, and I'd never pick an environment like this if I could chose. Microchip, which are a player in the controller market too, has all the documentation for free and even have the tools for free, and you can do whatever hardware supports without being locked-down to IPs. Most of Intels & AMDs stuff is open too, even if there now are CPU drivers that are closed source, and some other things that should be open.
User avatar
qookie
Member
Member
Posts: 72
Joined: Sun Apr 30, 2017 12:16 pm
Libera.chat IRC: qookie
Location: Poland

Re: Separate Stack Segment in Protected mode?

Post by qookie »

rdos wrote: We used Keil for ARM development, the "IPs" seems to be locked down and lacking in source as well as a possibility to the modify code. It's possible there are other more "open" environments, but what you mention about IPs seems to indicate this is the policy of ARM rather than of those that provide Keil.
Keil does not produce hardware afaik, and "IP" in this context is referring to the hardware designs specifically (ARM does not really provide development toolkits themselves). For example, ARM provides the IPs such as Cortex-A ARM cores, PrimeCell series of peripherals (PL011 UART, PL031 RTC, PL022 SSP), AMBA and related peripherals (SDRAM controllers, cache controllers etc).
Working on managarm.
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: Separate Stack Segment in Protected mode?

Post by Schol-R-LEA »

A qookie said, all ARM licenses out are the ISA itself and designs for some of the specific CPU cores, as well as some of the supporting chips and SoC components. The system developer documentation for the ARM cores is freely available from them as a set of PDFs.

It is not any different from how Intel and AMD co-license the x86 and x86-64 ISAs to each other, but provide full developer documentation publicly.

However, if open hardware is a major concern to you, what do you think of RISC-V? While it is not a major player yet, it is growing in use, especially for support microcontrollers. There are both 32-bit and 64-bit implementations, the latter of which (and some of the former) have full MMU support. Any opinions on this?
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Separate Stack Segment in Protected mode?

Post by rdos »

Schol-R-LEA wrote:A qookie said, all ARM licenses out are the ISA itself and designs for some of the specific CPU cores, as well as some of the supporting chips and SoC components. The system developer documentation for the ARM cores is freely available from them as a set of PDFs.

It is not any different from how Intel and AMD co-license the x86 and x86-64 ISAs to each other, but provide full developer documentation publicly.

However, if open hardware is a major concern to you, what do you think of RISC-V? While it is not a major player yet, it is growing in use, especially for support microcontrollers. There are both 32-bit and 64-bit implementations, the latter of which (and some of the former) have full MMU support. Any opinions on this?
My opinion on both ARM and RISC-V 32-bit & 64-bit CPUs is that I already have something that works for x86, so why would I want to invest years in writing a new operating system for them? I find it useful to work on simpler PIC controllers (typically in assembly), since those don't require an operating system and the designs you can do with them doesn't need a 32-bit CPU with a MMU or OS. For example, I did an USB-based communication device with a PIC that I use to control some embedded systems.

I've also designed more powerful devices. I used an FPGA to stream A/D data up to 750 MSamples / s to the PCI bus in real time. The manifacturer (Xilinx) does have a CPU core that you can add to the FPGA, but I didn't think this was something I wanted to involve in, and so I wrote the complete application in Verilog instead, and added a configuration interface that the x86 PC could use to change hardware settings through the PCI bar. Xilinx also have IPs, but I think those are a bit different and more reasonable. After all, you don't have to configure them with C code. You also don't need to mess with Linux, building cross-compilers for GCC and alike. However, Xilinx seem to assume that everybody wants to use their CPU core to configure FPGA devices and to handle the overall function of the device. All their example code for complex devices is based on the assumption that you use their CPU core. Of course, to use the CPU core, you need a cross compiler and a mini-Linux system.
linguofreak
Member
Member
Posts: 510
Joined: Wed Mar 09, 2011 3:55 am

Re: Separate Stack Segment in Protected mode?

Post by linguofreak »

Schol-R-LEA wrote:We've gone over this many, many times before, rdos. Segmentation is not, and has never been, a protection mechanism, any more than paging is. While the protection mechanisms work together with them, it is not the protection mechanism, nor does it provide any more or any less protection than paging does.
rdos wrote:The same scenario in long mode can lead to corruption of physical memory, vital kernel data, application data in another process, and even PCI BAR data.
That's simply not true, or rather, the claim that segmentation would prevent it is incorrect. Supervisor-mode pages have exactly the same degree of protection as supervisor-mode segments - a wild userland pointer to a supervisor data page is still going to be blocked by the protection mechanisms, because the page is marked as supervisor access only. A wild pointer in the kernel? True, that can access any virtual address currently mapped for the process, but the majority of addresses won't be mapped at all, meaning that a page fault will be caught by the memory manager, which presumably can determine that the page shouldn't be accessible and raise a protection fault. If it does hit an address that is live, then yes, a kernel bug can have the effect you describe - but the same is just as true with segmentation. A corrupted supervisor-mode pointer is a supervisor-mode pointer, period.
A corrupted supervisor far pointer is a corrupted supervisor far pointer, and, given the small size of the x86 descriptor tables, if you're making heavy use of segmentation, a large proportion of possible selector values are likely allocated (though if the corruption includes the low-order bits of the selector and affects those bits completely randomly, the RPL check will save you 75% of the time if the selector points to a ring-0 descriptor). This also applies to loading a segment before doing a bunch of near pointer work in that segment.

But, assuming a separate bug did not cause the incorrect segment to be loaded, a corrupted supervisor near pointer can only affect the relevant segment. If it tries to access an address beyond the segment's limit, you'll get a fault, and the fraction of addresses *within that segment* that are valid is going to be less than or equal to the total fraction of the logical address space that is valid (equal to it only if you have a segment covering the whole logical address space). In fact, with far pointers, this will actually stack with the RPL check and whatever proportion of unallocated selectors you do have.

There are definite protection benefits afforded by non-flat address spaces, but Intel segmentation is a clunky implementation of the non-flat address space concept:

1) The use of base-offset within a global paged address space impacts performance and means that the sum of all simultaneously loaded segments has to fit within the size of the paged address space. It would be better to have each "segment" be a full paged address space with no "global" paged address space (multiple CR3s, one per segment register, and a CR3 value rather than a base-offset as part of each segment descriptor).
2) The limited width of the segment registers, and the use of two bits in the selector for the RPL, makes the pool of segments that can be addressed at any one time far too limited. Wider segment registers would be better: with a 32 bit selector you could probably even keep the RPL if you wanted, with a 64-bit selector (possibly with a narrower width like 48 bits in the implementation, sign-extended for forward compatibility to a full 64-bit selector) you definitely could, though I'm not sure the function of the RPL couldn't be better implemented by other mechanisms.
3) Intel segmentation comes close to being a capability system, but isn't quite there. There are massive potential benefits for microkernels if you have a non-flat addressing scheme that does act as a capability system. This could be implemented by having a "System Descriptor Table", that has descriptors for ever segment/address space in the system containing the actual addressing information for that segment (base+offset if you're doing actual segmentation, or "CR3" if you're doing paged address spaces). You wouldn't be able to directly load an SDT selector into a segment register: every code segment, and every data segment used as a stack segment, would have a "Virtual Descriptor Table", that specifies what segments are loadable when that segment is loaded as CS/SS. The descriptors in the VDT wouldn't contain direct addressing information, but rather would contain a selector pointing into the SDT. (It's tempting to call these "Global" and "Local" descriptor tables, rather than "System" and "Virtual", but as Intel segmentation uses that terminology for a different arrangement, that would only invite confusion).

1) and 2) especially, and to some degree 3), are due to back-compatibility with the 8086 and 286, but there's a scheme, I think, that could work towards alleviating these issues while maintaining back-compatibility (probably not a big issue these days, but this could have been helpful for Intel in developing the 386, or for AMD when developing the x86-64):

If you go with 32 or 64-bit segment selectors, your segment tables are going to need a similar sort of multi-level scheme to what's used in page tables. So you split your VDT selectors into a lower and an upper part. The lower part is 16-bit, and indexes into the lowest level of the VDT. The upper part indexes into the remaining levels. The lowest level of the VDT indexed by the upper part (the second lowest level overall), has a two-bit "legacy type" field in its table entries. This can have values of "none", "protected", "real, megabyte aligned" or "real with offset". If the type is "none", then the table entry points to the lowest level of the VDT, indexed by the low 16 bits of the selector, and the entries in that table are VDT entries each pointing to an SDT selector. If the type is anything *other* than "none", the second-level VDT entry is a selector into a "legacy environment descriptor table" (and there is no lowest level of the VDT). If the type is "protected", then the LEDT entry contains an SDT selector (pointing to a paged address space), and a pointer to a legacy GDT, whose descriptors use the address space designated by the SDT selector as their logical address space. The low 16 bits of the VDT selector, instead of indexing in to the lowest level of the VDT, index into the designated GDT. If the type is "real, megabyte aligned", the LEDT entry contains an SDT selector and an offset (at megabyte granularity) into the designated address space. If the type is "real with offset", then the LEDT entry contains a SDT selector and an offset (at 16-byte granularity) into the designated address space.

In both "real" legacy modes, the low 16 bits of the VDT selector, rather than indexing into the lowest level of the GDT, or into an LDT, are simply added to (or in the case of "megabyte aligned", concatenated with) the offset in the LEDT entry. This allows for a single address space to host multiple real-mode environments at different offsets (megabyte-aligned mode has the advantage of requiring one less addition, but can't handle any real mode code that depends on the HMA mode existing, offset mode requires an extra addition but allows for an HMA).

If CS contains a selector whose upper part has a legacy type of "none", then the standard segment register manipulation instructions manipulate the whole segment register (though you might have prefixed instructions that manipulate either part individually). If CS contains a selector whose legacy part is anything other than "none", then the standard segment register manipulation instructions only deal with the lower 16 bits (though you might have prefixed instructions that manipulate the whole register or the upper part). So to run a program that uses legacy segmentation, you load all the segment registers with selectors whose upper parts have legacy types other than "none" and all point to the same LEDT (probably selectors whose upper parts are, in fact, identical), and then far jump into a code segment with an upper part that uses the same LEDT. Here you don't have a specific "real" or "compatibility mode", you just have special segment types.

It is, at least, an interesting road-not-taken.
As for drivers, well, either they are running in supervisor mode - whether intrinsic to the kernel as with a monolithic kernel, or loaded as modules, as with most hybrid models - or they are in a separate process, as with a microkernel system. For microkernels, the drivers would be covered by the protection mechanisms the same as the user processes are (even if one were to use the intermediate ring 1 or ring 2 levels). For supervisor-mode drivers - whether loadable or not - then it becomes a matter of trust, again regardless of whether segmentation is used or not.
With a capability-structured segmentation system, this wouldn't necessarily be the case.
The only way what you are describing could work is if the driver segments are run in supervisor mode, but mapped separately from the kernel to their own code, stack, and data segments. As far as I am aware, this isn't possible - supervisor-mode memory will all have the same memory mapping, meaning that the kernel would have the same segmentation as the drivers. I can't see any way you can have separate segments within the supervisor memory space for the drivers distinct from the kernel itself - nor can I see how this differs from doing the same with paging, if so. As iansjack said, you can just as easily use separate page tables as you can separate segments.
For well-intentioned (but possibly buggy) drivers using near pointers for their own data, segmentation does provide a fair bit added protection against wild pointers. For poorly written drivers that use far pointers everywhere, it will improve the probability of a wild pointer causing a fault (instead of further memory damage) somewhat, but not eliminate the danger entirely, and against malicious drivers it does nothing.
I will again ask you a question you dodged previously: aside from x86, what other modern ISAs which support virtual memory (i.e., not a microcontroller) have you worked with? It is no coincidence that none of them use segmentation, because more or less all of them have had 32-bit or 64-bit memory addressing from the outset, and didn't need a hack to make a larger address space out of overlapping 16-bit memory addresses.
ESA/390 and z/Architecture have fully-paged, non flat addressing. The 360/370/390/z line was never as cramped as a 16-bit address space, and it's 24-bit addressing days were behind it when the non-flat features were introduced, so I believe they were introduced for the benefits described above rather than to deal with a cramped address space. Of course, physical hardware isn't available for hobbyists (but Hercules exists), and z/Linux doesn't use the non-flat features (and the OSes that do are heavily proprietary and you can't get a license to run them on Hercules).

The implementation 390 and z/Arch use is largely what I have described above (minus the bits where I talked about how to accomplish back-compatibility with legacy Intel segmentation in such an implementation, and with some really opaque documentation. IBM manuals use a *ton* of non-standard terminology for common concepts, because they were there before everyone else, so they just kept using the terminology they had while the rest of the industry standardized on different terms).
What are you going to do if - or rather, when - Intel drops 32-bit protected mode, the same way they apparently plan to drop real mode? True, it probably won't be any time soon, but it is almost certainly coming - assuming that x86 remains the dominant desktop platform in the first place, which is increasingly unlikely with the growth of ARM platforms with comparable performance to the best x86-64 CPUs. What will you do if there are no more segmented platforms in common use?
Well, he always has the option of porting OpenWatcom to target z/Architecture, porting his OS to run on z/Architecture, and running his code under Hercules on the physical platform of his choice.
linguofreak
Member
Member
Posts: 510
Joined: Wed Mar 09, 2011 3:55 am

Re: Separate Stack Segment in Protected mode?

Post by linguofreak »

rdos wrote:
Schol-R-LEA wrote:We've gone over this many, many times before, rdos. Segmentation is not, and has never been, a protection mechanism, any more than paging is. While the protection mechanisms work together with them, it is not the protection mechanism, nor does it provide any more or any less protection than paging does.
I clearly claimed that I'm not discussing malicious code, rather the statistics that a bad pointer could corrupt vital kernel data or physical memory (in cases where all physical memory is mapped in the linear address space). In this regard, paging and paging + segmentation (yes, I'm not talking about segmentation on it's own) is better than running things with paging & segmentation turned off. This is obvious since every important system use paging and nothing runs without paging anymore. This is because if you run multiple processes without paging, then they can interfere with each other and take each other down. This is not acceptable. However, it seems like many people regard the same scenario in kernel space to be acceptable, which I find rather odd.
It's not ideal, but my understanding is that Microsoft requires 3rd party drivers to be vetted by MS before they'll allow the vendor to say that its product is Windows-compatible, Apple restricts OSX to running on their own hardware (so they can basically write their own drivers to a large degree, and even at the application level, the Apple environment is code-signed and walled-gardened to the point of absurdity), and most Linux-drivers are submitted to be in-tree, so Linus yells at people that submit bad code. Hobby OSes generally don't have much market share beyond the developer, so the selection of hardware they need to run on is minimal, and if one does become successful, it will probably adopt either the MS or the Linux model.

With a properly designed segmentation system (full paged address space per segment, rather than base-offset, wide segment registers, and a capability-structured segmentation system), you could do better, but on Intel the costs of making use of segmentation are generally perceived to outweigh the benefits, on z/Arch I don't believe that there are sanely-licensed toolchains available (above the assembly language level) that will make use of the non-flat features (plus the average person is unlikely to get ahold of bare z/Arch metal, so emulation will have to do), and most other architectures are flat-address-space.
16bitPM
Member
Member
Posts: 28
Joined: Wed Sep 05, 2012 3:53 pm

Re: Separate Stack Segment in Protected mode?

Post by 16bitPM »

Demindiro wrote:
rdos wrote:Perhaps, but also a good argument for why you don't want to use long mode. :-)
I think the performance benefit alone is enough to negate any arguable downsides.
My hobby is retrocomputing. 16-bit protected mode on a 80286 gets my **** hard. I know it's hard to understand for some people, but it's a hobby 8)
16bitPM
Member
Member
Posts: 28
Joined: Wed Sep 05, 2012 3:53 pm

Re: Separate Stack Segment in Protected mode?

Post by 16bitPM »

rdos wrote:
iansjack wrote:I can think of better arguments in favour of long mode than a 17-year-old research paper.
Some things never change. Running an OS kernel without effective protection mechanisms in place is insane. I don't count paging as an effective protection mechanism since it has poor granularity and no limit checking. A decent micro-kernel design *might* be acceptable, but neither Windows nor Linux use that design. The problem becomes even worse when people decide to map all physical memory in the address space, and pack code & data in the executables.
Plus, the segmentation hardware is practically transparent once everything set up.

OK... that's an overstatement, but it's still sad that everything had to go because of POSIX-compatibility (at least, that's how I understood things).

Anywaaaaay...

The loadle.asm module in the Causeway-source code on Github has a section "Convert object definitions into 3P segment definitions for CWD.". It clearly sets up separate segments, but giving the comment (related for CWD), this is maybe the module used by the debugger for extra protection? But if they can set it up for the debugger, it should be possible to do it for a generic loader. I guess.

I admit, I only skimmed through the code and I don't understand all of it.

Also, just before that section, we have "Setup entry SS:ESP" which has a different fixup based on the BIG flag.

I'm still mesmerized by this problem. Up until now, I only used simplified segment directives. I guess one could set up a completely independent segment (with the full segment definitions maybe?), then rebase the stack to that new segment manually?
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Separate Stack Segment in Protected mode?

Post by nexos »

rdos wrote:A decent micro-kernel design *might* be acceptable, but neither Windows nor Linux use that design. The problem becomes even worse when people decide to map all physical memory in the address space, and pack code & data in the executables.
Writing a microkernel would be way more secure than using segmentation. Period. I agree that segmentation's limit checking does have benefits, but by using a microkernel, we get the benefits of paging, and also prevent accidental (or even malicious!) corrupting of address space. Segmentation can't claim to prevent malicious corruption.

I do agree that mapping all physical memory is a failure-by-design; it makes it all too easy for a bad pointer in the kernel to destroy your hardware.

Packing code & data in executables... okay, give me an alternative to doing that. I can't think of any decent ones :) . That has been entrenched in computers since the Von Neumann architecture came out. The chance of changing that now is zero.
16bitPM wrote:OK... that's an overstatement, but it's still sad that everything had to go because of POSIX-compatibility
POSIX compatibility has nothing to do with segmentation. A fully POSIX compatible OS could still make decent use of segmentation.
Of course, some components (like mmap) might be a pain, but I can still think of ways to implement them with segmentation.

The real show-stopper for segmentation is the lack of toolchain support. GCC / Clang / CL, which are by far the three dominant compilers of the day, have no support for segmentation whatsoever. The only production-quality C compiler I can think of that does support segmentation is Open Watcom.

With that being said, segmentation provides one benefit over paging (limit checking). Paging, however, provides a huge number of benefits. For example; it's much easier to work with a flat address space than a segmented one. Not to mention that it's much faster too; on modern CPUs with PCID support, switching page mappings is very fast. On segmented system, you have to re-load the LDT every time, which isn't too expensive when segmentation is used lightly, but gets very expensive very quick when you have many segments in your address space. It also is much more granular; you control memory down to the page, which is very useful for swapping, memory protection, and other things. It also provides clean separation of physical memory and the address space, which is very useful to user applications.

If you are not convinced that paging is better, look at the mess that C development became in the old days of Win16. Any developer who values their time would not want to mess with that.

I rest my case :)
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: Separate Stack Segment in Protected mode?

Post by Schol-R-LEA »

@Linguofreak: I agree with Ivan Godard, who said (paraphrasing) "I would love to make a capability-based architecture, I know how to make a capability-based architecture, but I don't know of any way to sell a capability-based architecture." Security comes dead last in the minds of most consumers, and even most professional administrators. It's sad, but true.

As I've said before, hardware capabilities are what rdos really seems to want. They are not what x86 segmentation provides.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Separate Stack Segment in Protected mode?

Post by rdos »

Schol-R-LEA wrote: As I've said before, hardware capabilities are what rdos really seems to want. They are not what x86 segmentation provides.
I think I disagree. My capability structure consists of handles translated from a table. The handle structure also contains the type of the handle, and make sure it cannot be used with functions the handle doesn't work with. These handles are not much different from selectors. The major difference is that handles are typed while selectors are not. In fact, both use 16-bit integers. Additionally, most capability table entries points to a selector containing object data, which protects the object from being misused by the "server".

Intel or AMD could have extended selectors to 32-bits, or even 64-bits. This would have made them less prone to accidental access. They could event have keyed them with a GUID or something.

Besides, even if somebody provided a much better hardware capability implementation, C compilers and Posix still would not be able to use them, and so people would continue with their flat memory models.

There actually is a possibility to implement "segments" in long mode too. The 16 higher bits of the address could be used as a selector, and given 32-bit RIP addressing, would be quite safe. However, the GCC environment doesn't support this (at least did not when I tried it), and the linker is not able to being smart enough to structure code in this way, and rather packs everything into the smallest possible address space.
Last edited by rdos on Thu Aug 18, 2022 1:28 am, edited 1 time in total.
rdos
Member
Member
Posts: 3297
Joined: Wed Oct 01, 2008 1:55 pm

Re: Separate Stack Segment in Protected mode?

Post by rdos »

nexos wrote:
rdos wrote:A decent micro-kernel design *might* be acceptable, but neither Windows nor Linux use that design. The problem becomes even worse when people decide to map all physical memory in the address space, and pack code & data in the executables.
Writing a microkernel would be way more secure than using segmentation. Period. I agree that segmentation's limit checking does have benefits, but by using a microkernel, we get the benefits of paging, and also prevent accidental (or even malicious!) corrupting of address space. Segmentation can't claim to prevent malicious corruption.
I think you over-rate the abilities of microkernels in flat memory model environments. The microkernel still lives in a flat memory model kernel (even if small) that can contain bugs. The interface between servers and clients must go through this environment, and could threaten system stability. And I'm sure that the servers needs to use syscalls too.

However, I do agree that the server itself is very well protected.
nexos wrote: With that being said, segmentation provides one benefit over paging (limit checking). Paging, however, provides a huge number of benefits. For example; it's much easier to work with a flat address space than a segmented one. Not to mention that it's much faster too; on modern CPUs with PCID support, switching page mappings is very fast. On segmented system, you have to re-load the LDT every time, which isn't too expensive when segmentation is used lightly, but gets very expensive very quick when you have many segments in your address space. It also is much more granular; you control memory down to the page, which is very useful for swapping, memory protection, and other things. It also provides clean separation of physical memory and the address space, which is very useful to user applications.

If you are not convinced that paging is better, look at the mess that C development became in the old days of Win16. Any developer who values their time would not want to mess with that.

I rest my case :)
I don't think anybody that likes segmentation think it should run without paging. Paging is essential for separating processes. So, you should not compare segmentation with paging, rather what additional advantage the use of segmentation has over only using paging.
Post Reply