Usage of SS register in Long Mode

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
xryshkarelia
Posts: 2
Joined: Tue Jun 02, 2026 10:11 am
Libera.chat IRC: xryshh

Usage of SS register in Long Mode

Post by xryshkarelia »

Hello,

I am implementing a toy user space (R3) and having trouble to understand the usage of SS in long mode.
Here what I have found:

Windows segments in R0 (source: Windows Internals)

Lazy segment loading
Based on the description and values of the segments described earlier, it may be
surprising to investigate the values of DS and ES on an x86 and/or x64 system
and find that they do not necessarily match the defined values for their
respective ring levels. For example, an x86 user-mode thread would have the
following segments:
CS = 1Bh (18h | 3)
ES, DS = 23 (20h | 3)
FS = 3Bh (38h | 3)
Yet, during a system call in Ring 0, the following segments would be found:
CS = 08h (08h | 0)
ES, DS = 23 (20h | 3)
FS = 30h (30h | 0)
Similarly, an x64 thread executing in kernel mode would also have its ES and
DS segments set to 2Bh (28h | 3). This discrepancy is due to a feature known as
lazy segment loading and reflects the meaninglessness of the Descriptor
Privilege Level (DPL) of a data segment when the current Code Privilege Level
(CPL) is 0 combined with a system operating under a flat memory model. Since
a higher CPL can always access data of a lower DPL—but not the contrary—
setting DS and/or ES to their “proper” values upon entering the kernel would
also require restoring them when returning to user mode.
Although the MOV DS, 10h instruction seems trivial, the processor’s
microcode needs to perform a number of selector correctness checks when
encountering it, which would add significant processing costs to system call and
interrupt handling. As such, Windows always uses the Ring 3 data segment
values, avoiding these associated costs.
AMD on Segment Registers in 64-Bit Mode:
DS, ES, and SS Registers in 64-Bit Mode. In 64-bit mode, the contents of the ES, DS, and SS
segment registers are ignored. All fields (base, limit, and attribute) in the hidden portion of the
segment registers are ignored.
Address calculations in 64-bit mode that reference the ES, DS, or SS segments are treated as if the
segment base is 0. Instead of performing limit checks, the processor checks that all virtual-address
references are in canonical form.
Neither enabling and activating long mode nor switching between 64-bit and compatibility modes
changes the contents of the visible or hidden portions of the segment registers. These registers remain
unchanged during 64-bit mode execution unless explicit segment loads are performed.


Since AMD explicitly says that DS, ES, and SS are ignored and Windows keep the R3 DS selector in kernel to avoid cost, why Windows save the old (R3) SS selector in syscall?
source: Windows internals
Now that the kernel stack is loaded, the function builds a trap frame, using the
format described earlier in the chapter. This includes storing in the frame the
SegSs set to KGDT_R3_DATA (0x2B), Rsp from the UserRsp in the PCR,
EFlags from R11, SegCs set to KGDT_R3_CODE (0x33), and storing Rip from
RCX. Normally, a processor trap would’ve set these fields, but Windows must
emulate the behavior based on how syscall operates.
Has anyone tried to fill SS in STAR ( C000_0081h ) with a R3 selector? Or keep R0 SS selector in user mode?
Thanks.
Octocontrabass
Member
Member
Posts: 6275
Joined: Mon Mar 25, 2013 7:01 pm

Re: Usage of SS register in Long Mode

Post by Octocontrabass »

xryshkarelia wrote: ↑Tue Jun 02, 2026 10:35 amSince AMD explicitly says that DS, ES, and SS are ignored and Windows keep the R3 DS selector in kernel to avoid cost, why Windows save the old (R3) SS selector in syscall?
Windows uses the same trap frame for interrupts and syscalls. Interrupts save SS in the trap frame, so syscalls also need to save SS in the trap frame.
xryshkarelia wrote: ↑Tue Jun 02, 2026 10:35 amHas anyone tried to fill SS in STAR ( C000_0081h ) with a R3 selector?
The SYSCALL and SYSRET instructions already avoid the cost of loading segments. At best, it will do nothing. At worst, it will cause problems because you're doing something the CPU didn't expect you to do.
xryshkarelia wrote: ↑Tue Jun 02, 2026 10:35 amOr keep R0 SS selector in user mode?
The CPU requires a ring 3 SS selector in user mode. If you somehow manage to enter user mode with a ring 0 SS selector, the first interrupt you handle will fault at the IRETQ instruction because only ring 3 SS selectors are allowed in user mode.

I suggest you refer to the Intel SDM and AMD APM. It's a good idea to always check both - there are some differences between the two. (For example, take a look at how SYSCALL and SYSRET behave...)
User avatar
xryshkarelia
Posts: 2
Joined: Tue Jun 02, 2026 10:11 am
Libera.chat IRC: xryshh

Re: Usage of SS register in Long Mode

Post by xryshkarelia »

yeah you were right

Code: Select all

If a segment selector index is outside its descriptor table limits.
If a segment descriptor memory address is non-canonical.
If the stack segment is not a writable data segment.
If the stack segment descriptor DPL is not equal to the RPL of the return code segment
selector.
If the stack segment selector RPL is not equal to the RPL of the return code segment selector.
#SS(0) If an attempt to pop a value off the stack violates the SS limit.
If an attempt to pop a value off the stack causes a non-canonical address to be referenced.
#NP(selector) If the return code or stack segment is not present.
#PF(fault-code) If a page fault occurs.
#AC(
from SDM 2B

Should have read INTEL first. Appreciate it.
Post Reply