0

Section 4.7.2 of the the AMD64 Developer Manual Volume 2 (System Programming) which describes Code-Segment Descriptors in legacy modes states:

Code segments establish the processor operating mode and execution privilege- level. The segments generally contain only instructions and are execute-only, or execute and read- only. Software cannot write into a segment whose selector references a code-segment descriptor.

If code segments are the only executable segments, and they cannot be written to, how did executable stacks on 32 bit systems work?

Omar Darwish
  • 1,536
  • 2
  • 15
  • 23
  • I untagged x86-64 because long mode enforces a flat memory model, where `ss:off` is the same location as `cs:off`, so anything you write to the stack can be jumped to, depending on page permissions. – Peter Cordes Jul 24 '21 at 04:25

1 Answers1

2

Code segments are executable (but not writable). Stack segments must be read/write (and aren't executable).

Each segment has a base address and a limit. Software provides offset/s into segment/s (e.g. the instruction pointer is an offset into the area described by the cs segment, the stack pointer is an offset into the area described by the ss segment); where the CPU checks that the offset is within the segment's limit, then adds the base address of the segment to the offset to determine the (linear) address.

If the code segment and stack segment refer to the same memory (e.g. have the same base address and limit); then that same memory can be executed (using the code segment) and read/write (using the stack segment).

Note that most operating systems don't/didn't use segmentation (they set all segment base addresses to zero and all segment limits to "max" so that it behaves like there's no segments); and "all segment refer to the same memory" is very common. For protection, most operating systems use paging only.

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • Oh interesting, `mov [cs: esp], eax` segfaults (in compat mode under x86-64 Linux where `mov [ss: esp], eax` runs fine), I guess because the CS segment override applies the read-only segment permissions of the code segment. I hadn't thought about segment overrides implying permissions as well as a base. But that does stop a program with a non-flat memory model from modifying its own code unless it can address that linear address via another segment. – Peter Cordes Jul 24 '21 at 04:35
  • @PeterCordes: Yes - most people don't really notice things like that (when all segments refer to the same memory there's little reason to use segment override prefixes, and with "default/implied segments" everything is mostly well behaved). For more fun (without segment overrides), consider `mov ebp,0xFFFFFFFF` then `mov [ebp],eax` running on a "flat memory" OS that's trying to pretend that there's no segmentation - for "segmentation literally disabled" you'd expect wrapping, for "all segments are the same" you'd expect a general protection fault, and what you actually get is a stack fault. – Brendan Jul 24 '21 at 05:30