Page 2 of 3

Re: Your exprience on Segmentation vs. Paging

Posted: Sun Jan 06, 2013 2:38 pm
by 0x57
rdos wrote:Using segmentation without paging is a bad design-choice. The only really useful approach for a segmented design is to use segmentation on top of paging.
Thanks rdos for the answer, it was really helpful. By the way, you mean something like L4 microkernels? It uses the same page table for multiple processes I guess, but separates the address spaces using Segmentation, right?

Are there any other approaches for implementing Segmentation on top of Paging? Thanks.

Re: Your exprience on Segmentation vs. Paging

Posted: Sun Jan 06, 2013 4:50 pm
by Brendan
Hi,
rdos wrote:Using segmentation without paging is a bad design-choice. The only really useful approach for a segmented design is to use segmentation on top of paging.

Having gotten a long way with my 64-bit environment now (which uses only paging), and comparing it to my usual (segmented-paged) environment, I think I can conclude the pros and cons:

1. Syscalls in a segment-paged environment can be run without validation (the validation can be built-into the API by defining the appropriate selectors for user-mode). This is true even if applications are flat.

2. Syscalls in a paged-only environment needs extensive parameter evaluation in kernel before being passed to server.
This depends on what you're protecting.

If you're only protecting processes from other processes then:
  • for "flat paged" processes you only need a "top of memory" variable (e.g. set by something like a "sbrk()" function) and you'd do something like "if( address + size > processData->memoryTop ) return ERR_DENIED;"
  • for segmentation you'd have a small number of large segments and it's almost identical (e.g. "if( offset + size > segment->limit ) return ERR_DENIED;") unless you want a crappy kernel that crashes (general protection fault) because you failed to do validation
If you're protecting a process from itself (e.g. trying to detect things like "array index out of bounds" and accessing freed memory) then paging doesn't help at all. Segmentation could be used for this (e.g. new segment created by "malloc()" and segment destroyed by free()"), but it's likely to be a lot slower than using a managed language because of the frequent segment register loads and syscalls for creating and destroying segments, it won't detect lots of problems (e.g. a process trashing its own data) anyway and you end up running out of GDT/LDT entries. Basically, protecting a process from itself only matters for debugging (and the overhead of segmentation or managed languages isn't justified when you're not debugging) and using segmentation for this is a bad idea.
rdos wrote:3. Separation with segmentation in kernel is an adequate method of providing protection between modules in a kernel.

4. Separation with paging only in kernel is typically not an adequate method, instead many designs use a microkernel and let driver modules run in isolated user-address spaces. At least similar protection as with segmentation cannot be achieved without a microkernel design.
For monolithic kernels the basic idea is that kernel modules can be trusted and therefore "adequate" means no protection between modules is needed at all (and no overhead). For micro-kernels the basic idea is that these modules can't be trusted and therefore it's necessary to (for e.g.) prevent one module from accessing another module's data and also prevent a module from accessing a normal process' data.

Separating modules with segmentation can work, but if you trust the module then the overhead isn't necessary and if you don't trust the module then you have to make sure one module can't load another module's segments or load a normal process' segments (which means having an LDT for each module and each process, and doing "LLDT" every time control is passed). I actually used this for one of my earliest protected mode kernels and abandoned the idea because half the kernel's code ended up duplicated (different virtual memory management for normal processes and modules, different IPC for normal processes and modules, different executable file formats for normal processes and modules, different kernel APIs for normal processes and modules, etc). The other problem was space - the 4 GiB virtual address spaces were split into 2 GiB for the process, 1 GiB for the kernel, and 1 GiB shared by all of the "system modules"; which meant that you run out of space for modules very quickly (e.g. two video card drivers with 512 MiB of memory mapped IO each and 2 GiB of "VFS disk caches" and you're screwed). Of course I shifted to a micro-kernel, so a lot of code duplication disappeared and now each "system module" gets 3 GiB of space to use.
rdos wrote:5. Long mode addresses can be used in a pseudo-segment related manner by treating the upper 32-bits as a segment, and keeping various pieces at "random" locations. However, typical uses of 64-bit mode use the 2G lower addresses or 2G higher addresses, which provide no protection at all in addition to a 32-bit flat memory model.
Typical use of paging in long mode is "several TiB" for each 64-bit process, a full 4 GiB for each 32-bit process and about 512 GiB for kernel. Note: I don't know why people seem to limit the kernel to 512 GiB (especially for monolithic kernels), but I assume it's so that the kernel is contained in one page directory pointer table.
rdos wrote:Since there are pros and cons of each of these designs, it is not possible to tell which is best and which is most effective. It also depends on the speed of segmentation and paging on the particular processor, and how syscalls are constructed by the OS.
In theory I agree. In practice I don't think there are many ways to use segmentation where the advantages outweigh the disadvantages. The only good use of segments that I know of is L4's "small address spaces".


Cheers,

Brendan

Re: Your exprience on Segmentation vs. Paging

Posted: Mon Jan 07, 2013 4:06 am
by rdos
Brendan wrote: This depends on what you're protecting.

If you're only protecting processes from other processes then:
  • for "flat paged" processes you only need a "top of memory" variable (e.g. set by something like a "sbrk()" function) and you'd do something like "if( address + size > processData->memoryTop ) return ERR_DENIED;"
  • for segmentation you'd have a small number of large segments and it's almost identical (e.g. "if( offset + size > segment->limit ) return ERR_DENIED;") unless you want a crappy kernel that crashes (general protection fault) because you failed to do validation
It depends on market. For a embedded system, there is no sense in giving users "crash-dialogs", since they typically cannot do anything about the problem. In that scenario, it doesn't matter if a crash is in the kernel or in the application. The end result is still a reboot. Therefore, as long as crappy data from the application cannot make the kernel hang (only protection fault), there is no need to do these validations in the call interface. OTOH, I agree that a much more friendly interface (with signals to indicate bad parameters) could be designed with good parameter validation, but it also costs in term of execution speed.
Brendan wrote: If you're protecting a process from itself (e.g. trying to detect things like "array index out of bounds" and accessing freed memory) then paging doesn't help at all. Segmentation could be used for this (e.g. new segment created by "malloc()" and segment destroyed by free()"), but it's likely to be a lot slower than using a managed language because of the frequent segment register loads and syscalls for creating and destroying segments, it won't detect lots of problems (e.g. a process trashing its own data) anyway and you end up running out of GDT/LDT entries. Basically, protecting a process from itself only matters for debugging (and the overhead of segmentation or managed languages isn't justified when you're not debugging) and using segmentation for this is a bad idea.
Personally, I cannot image that segment register loads could be slower than interpreted code, or compiled byte-code. :mrgreen:
The primary problem lies in managing GDT and LDT entries.
Brendan wrote: For monolithic kernels the basic idea is that kernel modules can be trusted and therefore "adequate" means no protection between modules is needed at all (and no overhead). For micro-kernels the basic idea is that these modules can't be trusted and therefore it's necessary to (for e.g.) prevent one module from accessing another module's data and also prevent a module from accessing a normal process' data.

Separating modules with segmentation can work, but if you trust the module then the overhead isn't necessary and if you don't trust the module then you have to make sure one module can't load another module's segments or load a normal process' segments (which means having an LDT for each module and each process, and doing "LLDT" every time control is passed). I actually used this for one of my earliest protected mode kernels and abandoned the idea because half the kernel's code ended up duplicated (different virtual memory management for normal processes and modules, different IPC for normal processes and modules, different executable file formats for normal processes and modules, different kernel APIs for normal processes and modules, etc). The other problem was space - the 4 GiB virtual address spaces were split into 2 GiB for the process, 1 GiB for the kernel, and 1 GiB shared by all of the "system modules"; which meant that you run out of space for modules very quickly (e.g. two video card drivers with 512 MiB of memory mapped IO each and 2 GiB of "VFS disk caches" and you're screwed). Of course I shifted to a micro-kernel, so a lot of code duplication disappeared and now each "system module" gets 3 GiB of space to use.
No, LDTs are not for module usage, they are for application only usage (specifically for segmented applications). Just by assigning each driver module it's own code selector the module no longer can jump to garbage code in another module. By assigning default data segment to its own selector you make sure that data overwrites or corrupt pointers cannot affect other module data. Then you could also map major objects in the system to distinct selectors. The trick is which global objects to map to selectors and which not to. An additional advantage is to have configuration options in order to debug certain modules by mapping more objects to selectors, and a release option that uses less selectors.

In regard to VFS caches, those cannot be mapped to selectors, but need to be linear. A segmented kernel can still have a flat selector for use with linear addresses. The case of open files is somewhat contentious. Most usage would work if file-access structures are mapped to distinct GDT selectors while some (which uses many files) might not. But the VFS and actually FS drivers are good candidates for a "microkernel" design using flat address spaces.
Brendan wrote:
rdos wrote:5. Long mode addresses can be used in a pseudo-segment related manner by treating the upper 32-bits as a segment, and keeping various pieces at "random" locations. However, typical uses of 64-bit mode use the 2G lower addresses or 2G higher addresses, which provide no protection at all in addition to a 32-bit flat memory model.
Typical use of paging in long mode is "several TiB" for each 64-bit process, a full 4 GiB for each 32-bit process and about 512 GiB for kernel. Note: I don't know why people seem to limit the kernel to 512 GiB (especially for monolithic kernels), but I assume it's so that the kernel is contained in one page directory pointer table.
Not in my research on the issue. GCCs 64-bit support didn't support boot-strapping the compiler in medium or large memory model (libgcc code broken). All popular OSes using GCC is using the small memory model, which require that all code and static data be accessible either in the lower 2G or the upper 2G. Linux for sure uses the upper 2G in small memory model. Sure, the heap could use the entire address space, but the application / kernel code and static data cannot. That means that garbage 32-bit integers could be used as pointers. If the code instead is placed at some (random) position in the middle of the address space, the risk of garbage data being interpreted as valid pointers diminishes to a very low probability.

Additionally, LD also have an option for putting guard-pages between sections in the image, which doesn't work, and consequently, nobody use anymore.

Re: Your exprience on Segmentation vs. Paging

Posted: Mon Jan 07, 2013 4:16 am
by linguofreak
Brendan wrote:
rdos wrote:3. Separation with segmentation in kernel is an adequate method of providing protection between modules in a kernel.

4. Separation with paging only in kernel is typically not an adequate method, instead many designs use a microkernel and let driver modules run in isolated user-address spaces. At least similar protection as with segmentation cannot be achieved without a microkernel design.
For monolithic kernels the basic idea is that kernel modules can be trusted and therefore "adequate" means no protection between modules is needed at all (and no overhead). For micro-kernels the basic idea is that these modules can't be trusted and therefore it's necessary to (for e.g.) prevent one module from accessing another module's data and also prevent a module from accessing a normal process' data.

Separating modules with segmentation can work, but if you trust the module then the overhead isn't necessary and if you don't trust the module then you have to make sure one module can't load another module's segments or load a normal process' segments (which means having an LDT for each module and each process, and doing "LLDT" every time control is passed).
Yeah, this is one of my big frustrations with 386 segmentation. If I were designing a segmented paged architecture, I'd have things structured something like this:

Instead of dividing things up into a local and a global descriptor table, I'd put all segment descriptors into the GDT (though I might break it up into several tables so that a contiguous block of memory didn't have to be reserved for it). Then, instead of a local *descriptor* table, I'd have each code segment (and possibly each stack segment) have a pointer to a local *selector* table (LST). There would be two kinds of selectors, global selectors, which would be valid for direct use as indexes into the GDT (though loading a segment register this way would only be permitted in kernel mode), and local selectors, which would be per-process (or rather per-code/stack segment) and would index into the LST. Each entry in the LST would then contain a global selector value and various permission bits (so that a globally read/write segment could be restricted to read-only access for a particular program, for instance).

When a local selector was used in a segment register load (this would be required in user-mode), the hardware would do the following:

1)Use the local selector as an index into the LST to select an LST entry.
2)Use the global selector found in the selected LST entry as an index into the GDT.
3)Load the segment register in question from the selected GDT entry. If any permissions in the LST entry are more restrictive that those in the GDT entry, load those fields of the segment register with the corresponding fields of the LST entry instead.

This would allow a user-mode program to call a user-mode driver directly without message-passing through the kernel or a full context switch, while still preventing the program in question from calling just any code on the system it wanted to, and while preventing the driver from accessing private program data.

I can still think of a few bugs that would have to be worked out (largely relating to far returns), but something like this would be necessary for segmentation to be useful on a paged architecture.
I actually used this for one of my earliest protected mode kernels and abandoned the idea because half the kernel's code ended up duplicated (different virtual memory management for normal processes and modules, different IPC for normal processes and modules, different executable file formats for normal processes and modules, different kernel APIs for normal processes and modules, etc). The other problem was space - the 4 GiB virtual address spaces were split into 2 GiB for the process, 1 GiB for the kernel, and 1 GiB shared by all of the "system modules"; which meant that you run out of space for modules very quickly (e.g. two video card drivers with 512 MiB of memory mapped IO each and 2 GiB of "VFS disk caches" and you're screwed). Of course I shifted to a micro-kernel, so a lot of code duplication disappeared and now each "system module" gets 3 GiB of space to use.
This is another reason I'd like segment descriptors to reference page directories rather than offsets and limits into a paged address space: It would allow each segment to be up to 4 GiB (on a 32-bit system) without having to overlap with any other segment (assuming the physical address space is more than 32-bits wide, or that swap space is available).
rdos wrote:5. Long mode addresses can be used in a pseudo-segment related manner by treating the upper 32-bits as a segment, and keeping various pieces at "random" locations. However, typical uses of 64-bit mode use the 2G lower addresses or 2G higher addresses, which provide no protection at all in addition to a 32-bit flat memory model.
Typical use of paging in long mode is "several TiB" for each 64-bit process, a full 4 GiB for each 32-bit process and about 512 GiB for kernel. Note: I don't know why people seem to limit the kernel to 512 GiB (especially for monolithic kernels), but I assume it's so that the kernel is contained in one page directory pointer table.
It probably also has to do with the fact that few systems yet have that much physical memory, let alone need it for kernel data.
In practice I don't think there are many ways to use segmentation where the advantages outweigh the disadvantages.
Not on any existing hardware, as far as I can see. I love the idea of segmentation and microkernels (which, I think, need a really powerful segmentation mechanism to work), but there's no hardware on which anything but a flat-address space and monolithic kernel is feasible.

Re: Your exprience on Segmentation vs. Paging

Posted: Mon Jan 07, 2013 4:26 am
by rdos
0x57 wrote:
rdos wrote:Using segmentation without paging is a bad design-choice. The only really useful approach for a segmented design is to use segmentation on top of paging.
Thanks rdos for the answer, it was really helpful. By the way, you mean something like L4 microkernels? It uses the same page table for multiple processes I guess, but separates the address spaces using Segmentation, right?

Are there any other approaches for implementing Segmentation on top of Paging? Thanks.
I don't use the same page table for multiple processes. Each process has its own paging structure, just like a typical paged implementation. This was necessary already from the beginning in order to run multiple V86-processes.

Segmentation, in my design, is only used in order to protect modules from each others. I also have dropped segmented applications since perhaps 10 years ago, and now use 32-bit PE format. The trick here is that all application accessible selectors only can access the application address space (they have a 3G limit at most), so if an application sends kernel a linear address in kernel space, the kernel will use the user-selector when addressing, which would result in a protection fault. In fact, the kernel always works with 48-bit pointers (16-bit selector + 32-bit offset), even in calls between kernel modules.

A curiosity is that at least initially, the pointer validator in my new long mode module would allocate a LDT-descriptor for all pointer parameters, and then pass a 48-bit segmented address to the 32-bit server module which cannot be accessed out-of-limits. That only costs a single selector load, which doesn't affect syscall-speed a lot considering the total overhead. In fact, the alternative is to load the flat selector, which would take almost as long.

Re: Your exprience on Segmentation vs. Paging

Posted: Mon Jan 07, 2013 4:45 am
by rdos
linguofreak wrote: Yeah, this is one of my big frustrations with 386 segmentation. If I were designing a segmented paged architecture, I'd have things structured something like this:

Instead of dividing things up into a local and a global descriptor table, I'd put all segment descriptors into the GDT (though I might break it up into several tables so that a contiguous block of memory didn't have to be reserved for it). Then, instead of a local *descriptor* table, I'd have each code segment (and possibly each stack segment) have a pointer to a local *selector* table (LST). There would be two kinds of selectors, global selectors, which would be valid for direct use as indexes into the GDT (though loading a segment register this way would only be permitted in kernel mode), and local selectors, which would be per-process (or rather per-code/stack segment) and would index into the LST. Each entry in the LST would then contain a global selector value and various permission bits (so that a globally read/write segment could be restricted to read-only access for a particular program, for instance).

When a local selector was used in a segment register load (this would be required in user-mode), the hardware would do the following:

1)Use the local selector as an index into the LST to select an LST entry.
2)Use the global selector found in the selected LST entry as an index into the GDT.
3)Load the segment register in question from the selected GDT entry. If any permissions in the LST entry are more restrictive that those in the GDT entry, load those fields of the segment register with the corresponding fields of the LST entry instead.
I don't understand what this would improve. It is assumed that LDT is per process or even per application (it's up to the OS to decide and implement). The OS only use lldt to load a new local table.
linguofreak wrote: This would allow a user-mode program to call a user-mode driver directly without message-passing through the kernel or a full context switch, while still preventing the program in question from calling just any code on the system it wanted to, and while preventing the driver from accessing private program data.
That's already possible. The mechanism is called "call-gate", and provide exactly this functionality. In fact, this is the default method I use in rdos. If you want to disallow local data usage in kernel, you just load ldt with 0 (this would make it necesary to transform pointers first though).

Re: Your exprience on Segmentation vs. Paging

Posted: Mon Jan 07, 2013 8:04 am
by OSwhatever
While this discussion is originally meant for x86, I think that the segmentation model is still very interesting. The segmentation model in x86 is there just for backwards compatibility and it is no longer optimized or even supported. With new languages that provides better run time protection on object level, I think that HW "segmentation" is still a valid approach, but that would be with a new ISA. Basically, you reference objects by reference id in the instruction which has already been done in a number of Java CPUs.

How this model translates to performance versus paging is still a field that has a lot of room for research. There are few CPUs that support this model right now.

If there any OS developer out there who wants to try this model I would like to encourage it.

Re: Your exprience on Segmentation vs. Paging

Posted: Mon Jan 07, 2013 11:49 pm
by linguofreak
rdos wrote:
linguofreak wrote: Yeah, this is one of my big frustrations with 386 segmentation. If I were designing a segmented paged architecture, I'd have things structured something like this:

Instead of dividing things up into a local and a global descriptor table, I'd put all segment descriptors into the GDT (though I might break it up into several tables so that a contiguous block of memory didn't have to be reserved for it). Then, instead of a local *descriptor* table, I'd have each code segment (and possibly each stack segment) have a pointer to a local *selector* table (LST). There would be two kinds of selectors, global selectors, which would be valid for direct use as indexes into the GDT (though loading a segment register this way would only be permitted in kernel mode), and local selectors, which would be per-process (or rather per-code/stack segment) and would index into the LST. Each entry in the LST would then contain a global selector value and various permission bits (so that a globally read/write segment could be restricted to read-only access for a particular program, for instance).

When a local selector was used in a segment register load (this would be required in user-mode), the hardware would do the following:

1)Use the local selector as an index into the LST to select an LST entry.
2)Use the global selector found in the selected LST entry as an index into the GDT.
3)Load the segment register in question from the selected GDT entry. If any permissions in the LST entry are more restrictive that those in the GDT entry, load those fields of the segment register with the corresponding fields of the LST entry instead.
I don't understand what this would improve. It is assumed that LDT is per process or even per application (it's up to the OS to decide and implement). The OS only use lldt to load a new local table.
One thing it improves is that the LST switch is done automatically by hardware on a far call and/or stack switch (the LST pointer being part of the segment descriptor for the target code or stack segment), with the result that the LST switch can be done without entering kernel mode (LLDT requires privilege level 0 on x86's).
linguofreak wrote: This would allow a user-mode program to call a user-mode driver directly without message-passing through the kernel or a full context switch, while still preventing the program in question from calling just any code on the system it wanted to, and while preventing the driver from accessing private program data.
That's already possible. The mechanism is called "call-gate", and provide exactly this functionality.


No, LLDT is privilege 0 only (and, as implemented, it would allow a process to load any other processes LDT if it weren't privilege 0 only). So the program either needs to call the other program indirectly through the kernel (to have the kernel execute LLDT), or let the other program execute with the calling program's LDT in place.

If anything on x86's is equivalent to what I've described, it would be a far call through a task gate.

Re: Your exprience on Segmentation vs. Paging

Posted: Fri Jan 11, 2013 4:49 pm
by tom9876543
Segmentation is a waste of time in the 21st Century.

I am fairly certain the ARM architecture doesn't have segmentation.

AMD64 has virtually no segmentation.

The Motorla 68000 never had segmentation and it could be connected to the M68451 MMU to implement paging.

The Intel 286 Processor was a disaster, it was badly designed. Paging had existing on mainframes since the 1960s and Motorola had the M68451 MMU.
Intel should have had enough foresight to build a CPU with paging as the ONLY protection mechanism. Segment registers only maintain 8086 compatibility (simple << 4 and add). The GDT was an extremely bad design decision. As we know Intel realised their mistake and released the 386.

Re: Your exprience on Segmentation vs. Paging

Posted: Sat Jan 12, 2013 4:33 am
by rdos
tom9876543 wrote:Segmentation is a waste of time in the 21st Century.

I am fairly certain the ARM architecture doesn't have segmentation.

AMD64 has virtually no segmentation.

The Motorla 68000 never had segmentation and it could be connected to the M68451 MMU to implement paging.
Why do you think we still have a very popular x86 architecture while basically all the architectures of similar age has disappeared? Simple answer: x86 is a recipe for success. Hardware compability is much more successful than software compability.
tom9876543 wrote:The Intel 286 Processor was a disaster, it was badly designed. Paging had existing on mainframes since the 1960s and Motorola had the M68451 MMU.
I agree that Intel 286 was badly designed. Although it wouldn't have helped if it also had paging, as then we would have yet another paging mode.
tom9876543 wrote:Intel should have had enough foresight to build a CPU with paging as the ONLY protection mechanism. Segment registers only maintain 8086 compatibility (simple << 4 and add). The GDT was an extremely bad design decision. As we know Intel realised their mistake and released the 386.
Paging is not an effective protection mechanism. It is a complete disaster for protection unless multiple address spaces are also used. Basically, paging only has no better protection than real mode where anything can be overwritten by anybody.

What's more, the way most OSes uses long mode is no better as typical OSes typically doesn't use the additional address space in a smart way to increase protection.

The culpit rather is the lousy C language which cannot handle neither segmentation nor page level data protection in an effective way. For instance, the typical heap manager for C will generate localized storage sections where both other's data and control-blocks can be overwritten when pointers fail in C.

Re: Your exprience on Segmentation vs. Paging

Posted: Sat Jan 12, 2013 5:23 am
by Brendan
Hi,
rdos wrote:
tom9876543 wrote:Segmentation is a waste of time in the 21st Century.
Why do you think we still have a very popular x86 architecture while basically all the architectures of similar age has disappeared? Simple answer: x86 is a recipe for success. Hardware compability is much more successful than software compability.
I think you've got your centuries mixed up.

For the latter part of the 20th century, hardware compatibility with old software (e.g. real mode DOS software and 16-bit Windows) was important, and therefore segmentation was important. For the 21st century, hardware compatibility with old software is still important, but "old software" is 32-bit Windows software (e.g. designed for Windows 95) that never used segmentation.

Of course this is just from the end user's perspective. For an OS developer that doesn't have to care about backward compatibility things are different - segmentation would've become a waste of time in around 1990 (around 5 years after 80386 was released).
rdos wrote:Paging is not an effective protection mechanism. It is a complete disaster for protection unless multiple address spaces are also used. Basically, paging only has no better protection than real mode where anything can be overwritten by anybody.
Do you think that paragraph makes sense? Try this one: "Segmentation is not an effective protection mechanism. It is a complete disaster for protection unless multiple segments are used. Basically, segmentation has no better protection than real mode where anything can be overwritten by anybody."

Of course with paging and only one virtual address space you can protect supervisor pages from CPL=3 code, and with segmentation and only one segment you can't even do that. Therefore a system that uses paging but fails to use it properly and only uses one virtual address space is still better than a system that uses segmentation but fails to use it properly and only uses one segment.
rdos wrote:What's more, the way most OSes uses long mode is no better as typical OSes typically doesn't use the additional address space in a smart way to increase protection.
There are no smart ways to use the additional address space to increase protection. Crossing your fingers and hoping that malicious code won't try to access something in a different part of the virtual address space is purely idiotic.
rdos wrote:The culpit rather is the lousy C language which cannot handle neither segmentation nor page level data protection in an effective way. For instance, the typical heap manager for C will generate localized storage sections where both other's data and control-blocks can be overwritten when pointers fail in C.
This is not because C (the language) is lousy - it's perfectly capable of using segmentation and page level data protection (whatever that is). The problem is that compilers don't support this because nobody cares, and nobody cares because segmentation is a waste of time in the 21st Century.

[EDIT: Fixed the quotes! Sorry tom9876543.[/EDIT]


Cheers,

Brendan

Re: Your exprience on Segmentation vs. Paging

Posted: Sat Jan 12, 2013 11:19 am
by rdos
Brendan wrote: There are no smart ways to use the additional address space to increase protection. Crossing your fingers and hoping that malicious code won't try to access something in a different part of the virtual address space is purely idiotic.
Of course there is. It is all about statistics. If all your kernel is within -2 to 2GB, and the datastructures are packed close together, the probability that randoms integers could be valid addresses is quite large (and increases with size of kernel). The probability that pointers accessed out of limits will create "silent overwrites" is also large. The way to deal with these problems is to use random addresses in the middle of the address-space, not the top -2GB or bottom 2GB.
Brendan wrote: This is not because C (the language) is lousy - it's perfectly capable of using segmentation and page level data protection (whatever that is). The problem is that compilers don't support this because nobody cares, and nobody cares because segmentation is a waste of time in the 21st Century.
We have managed (interpreted languages) because C sucks at fundamental data protection issues. Issues that could be solved with hardware, but cannot because no matter how smart hardware we develop, C cannot use it intelligently.

Re: Your exprience on Segmentation vs. Paging

Posted: Sat Jan 12, 2013 11:24 am
by Griwes
We have managed languages, because most of programmers are piece of shite and really cannot write anything working and not killing the system (other than Hello World) in C or C++ or something at their level.

Re: Your exprience on Segmentation vs. Paging

Posted: Sat Jan 12, 2013 1:29 pm
by Brendan
Hi,
rdos wrote:
Brendan wrote: There are no smart ways to use the additional address space to increase protection. Crossing your fingers and hoping that malicious code won't try to access something in a different part of the virtual address space is purely idiotic.
Of course there is. It is all about statistics. If all your kernel is within -2 to 2GB, and the datastructures are packed close together, the probability that randoms integers could be valid addresses is quite large (and increases with size of kernel). The probability that pointers accessed out of limits will create "silent overwrites" is also large. The way to deal with these problems is to use random addresses in the middle of the address-space, not the top -2GB or bottom 2GB.
So, someone writing malicious code runs your OS inside Bochs, figures out which addresses they need to mess with in about 5 minutes; and then your OS sets the world record for being pawned.

It's like bending over in a gay bar with your pants around your ankles thinking to yourself "the chance of a dog biting my butt is extremely small, so I have nothing to worry about!".
rdos wrote:We have managed (interpreted languages) because C sucks at fundamental data protection issues. Issues that could be solved with hardware, but cannot because no matter how smart hardware we develop, C cannot use it intelligently.
In C, each piece of data can be in its own separate segment (including putting each thing marked as "const" in its own read-only data segment), each function can be in its own separate ("execute only") segment, and each piece of memory returned by "malloc()" can be a separate ("read/write") segment; and you wouldn't even need to bend the language's specification slightly.

I'll try to say it again - your issue is with C compilers (and the fact that nobody including compiler developers care about segmentation), and has nothing to do with C the language.


Cheers,

Brendan

Re: Your exprience on Segmentation vs. Paging

Posted: Sat Jan 12, 2013 3:07 pm
by linguofreak
Brendan wrote:Do you think that paragraph makes sense? Try this one: "Segmentation is not an effective protection mechanism. It is a complete disaster for protection unless multiple segments are used. Basically, segmentation has no better protection than real mode where anything can be overwritten by anybody."

Of course with paging and only one virtual address space you can protect supervisor pages from CPL=3 code, and with segmentation and only one segment you can't even do that. Therefore a system that uses paging but fails to use it properly and only uses one virtual address space is still better than a system that uses segmentation but fails to use it properly and only uses one segment.
You seem to have mismatched quote tags in your post. Stuff that rdos said is attributed to tom9876543.