Linux have (in addition to other segments) a user data segment and a kernel data segment.

When the CPU is in user mode, Linux loads the segment selector of the user data segment into the DS register. And when the CPU is in kernel mode, Linux loads the segment selector of the kernel data segment into the DS register.

But the kernel data segment is still accessible from user mode, since the user data segment and the kernel data segment point to the same virtual memory addresses!

So why does Linux have two data segments, one for user mode and another for kernel mode?

  • (1) How do other operating systems work?  If most operating systems work the same as Linux, then this isn’t a question about Linux. (2) You say, “But the kernel data segment is still accessible from user mode, since the user data segment and the kernel data segment point to the same virtual memory addresses!”  What do you mean?  Can you support this statement with references? Apr 6, 2019 at 23:56

1 Answer 1


Linux uses the same segment descriptor for SS (the stack segment) and DS (the data segment). The SS segment descriptor must have a DPL field exactly equal to CPL, i.e. the Current Privilege Level. Therefore you need separate data segment descriptors for kernel and user mode.

Sources (I am too lazy to download CPU manuals)

A comment inside the definition of gdt_page:

We need valid kernel segments for data and code in long mode too. IRET will check the segment types. -- kkeil 2000/10/28

Web search: iret checks segment types

Abhishek Yadav :

Instructions that load selectors into SS must refer to data segment descriptors for writable data segments. The descriptor privilege (DPL) and RPL must equal CPL. All other descriptor types or a privilege level violation will cause exception 13.

Web search: iret check segment type OR "DPL" OR "CPL" "SS"

"Many But Finite" :

... The exception is for the stack segment register ss, for which the three of CPL, RPL, and DPL must match exactly.

bochs-2.6.2/cpu/iret.cc:256 :

    /* stack segment DPL must equal the RPL of the return CS selector,
     * else #GP(SS selector) */
    if (ss_descriptor.dpl != cs_selector.rpl) {
       BX_ERROR(("iret: SS.dpl != CS selector RPL"));
       exception(BX_GP_EXCEPTION, raw_ss_selector & 0xfffc);

Background: why does Linux have separate code segments for user and kernel mode?

Related, another comment in the kernel source code:

We cannot use the same code segment descriptor for user and kernel mode, not even in long flat mode, because of different DPL.

This is because the DPL of the current code segment is used as the CPL.

I notice you also need different code segments for 32 and 64 bit code -

https://en.wikipedia.org/wiki/Segment_descriptor -

L=Long-mode segment

If set, this is a 64-bit segment (and D must be zero), and code in this segment uses the 64-bit instruction encoding

I guess the DS segment register gets reset when entering the kernel from userspace, at least on x86-32. But I was not able to identify the code that does it.

There is also a recent LWN.net article, which makes an interesting comment about set_fs().

The original role of set_fs() was to set the x86 processor's FS segment register which, in the early days, was used to control the range of virtual addresses that could be accessed by unprivileged code. The kernel has ... long since stopped using x86 segments this way.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .