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.
I have read that the TSS contains information about registers, etc. Right now, I am trying to implement the switch from kernel to user mode and back. I have read the the Intel 80386 manual, and was looking at this resource: http://www.brokenthorn.com/Resources/OSDev23.html for a general workflow. They do this:
In my case, when I am in user mode and I want to use a trap gate to get to kernel mode, the cs segment in the trap gate would have RPL 0 (the last 2 bits of the 16 bit segment) and the GDT entry corresponding to the cs segment would also have DPL 0. And I've read that an inter-level privilege switch switches stacks (only??) looking at the TSS. I'm guessing that the above piece of code must have a TSS.ss = 0x10.
Note: We're assuming the classic 0x08 = Kernel code, 0x10 = Kernel data, .... GDT structure here
If you forego hardware multitasking, the only bits of the TSS that are at all relevant are SS0, ESP0, SS1, ESP1, SS2, and ESP2. And the latter four only if you use more than two privilege levels. It works quite simply: If you run at any privilege level, and then an interrupt occurs with an IDT entry with IDTentry.CS.RPL < CPL, then a stack switch to the stack of level IDTentry.CS.RPL occurs. Therefore, if you run in Ring 0, no stack change will ever occur. In most cases these days, you will have all interrupt handlers running in Ring 0, thereby causing a stack switch when. previously, you were running in Ring 3.
The only exception to this are task gates. Task gates are typically used by certain hyper-critical exceptions (like NMI or MCE), that can occur at any time, including when it would be most awkward for the kernel. For those of course the entire register state matters. How they work is, they cause a task switch, so the entire register state is saved into the main TSS. For that it really matters not what is already in there (except for the privileged parts), since that will all be overwritten. It is only read again by the IRET instruction with the NT flag set.
TL;DR: That initialization is useless and can be removed. You only need to take care of the privileged stacks.
No, the author made SS=0x13. SS0 will be set to kernelSS, and at least on the linked page I couldn't find out what that is going to be. Now, SS will not matter. But yes, SS0 should be 0x10. Also, segment 0x13 does not make much sense, since segment 0x10 is a kernel segment and has no business requesting level 3.
Yeah, 0x10 it is...
Consider an x86 CPU in PMode.
I have another issue, and it's regarding this RPL, CPL business. Imagine you're switching to user mode from kernel mode (the first switch).
You push the push the stack information, the eflags and the PC (cs+eip) onto the stack. Now the processor looks at the RPL of the CS segment (what you pushed on the stack). Why RPL? Technically, I would want to look at the cs selector, look at the GDT/LDT and then look at the DPL. I would then find out which privilege level to switch to. Looks like we have to provide the same information twice?
Do we assume that RPL would be EQUAL to DPL? This RPL is just two bits that can be changed really easily (compared to a segment DPL).
I can think of situations to exploit this :
-The CS on the stack has DPL 3, but I make the RPL 0. The processor executing in ring 0 would see the RPL and consider a intra-level privilege switch and wouldn't switch stacks. But if would load a CS segment with DPL 3, making the CPL 3?? How would this work out??
-The CS on the stack has DPL 0, but I make the RPL 3. The processor executing in ring 0 would see the RPL and consider a inter-level privilege switch and switches stacks. It would still load a CS with the same PL (0) and continue executing in level 0 with but with RPL 3? Maybe this would limit data accesses?
Note: Mostly when I say RPL, I mean the last two bits of the 16 bit segment selector / register
You are right, the CPL, DPL, and RPL system is overcomplicated (PPC makes do with just a single bit), but not really unsafe. But remember, as CPL 0 you already have the highest access level. And also, RPL and DPL of the code segment have to be equal, since the Intel SDM states that IRET causes a #GP fault if the DPL for a non-conforming segment is not equal to the RPL of the code segment selector. The boffins at Intel really thought long and hard about their privilege system and I do think it is airtight, since so far, no attack against it has been demonstrated (but attacks against specific implementations are another thing). Yes, the kernel has to provide consistent data, but it has to do that anyway, with anything.
Meanwhile, on PPC you have a single bit in the machine state register, and when it is set, you are in Problem State, and when not, you are in System State, and in Problem State you are not allowed to fiddle with the thing or any other system resource. It could be so simple...
"Because interrupts generally do not occur at predictable times, this privilege rule effectively imposes restrictions on the privilege levels at which exception and interrupt handling procedures can run. Either of the following techniques can be used to keep the privilege rule from being violated.
• The exception or interrupt handler can be placed in a conforming code segment. This technique can be used by handlers for certain exceptions (divide error, for example). These handlers must use only the data available on the stack. If the handler needs data from a data segment, the data segment would have to have privilege level 3, which would make it unprotected.
• The handler can be placed in a code segment with privilege level 0. This handler would always run, no matter what CPL the program has." - Quoted from the Intel 30836 Programmer's Manual
How does the second statement work??? I thought that the DPL of the interrupt/trap gate in the IDT determined the "level of access" to an interrupt routine. If I was running at a CPL of 3, I could only access software interrupts whose IDT entry has a DPL of 3. What about hardware interrupts? Like the timer interrupt?
sunnysideup wrote:I am guessing that the sole purpose of the DPL field in the IDT entry is for the **int** instruction
The DPL in the Interrupt gate descriptor is not checked when an external interrupt occurs. With an external interrupt (and hardware generated exception), the DPL of the code segment specified in the interrupt gate is still checked.
You can learn more about what happens with different types of interrupts and exceptions (including external interrupts) in the Intel Software Development Manuals. 6.12.1.2 has this note:
For hardware-generated interrupts and processor-detected exceptions, the processor ignores the DPL of interrupt and trap gates.
All the volumes of the Software Development Guide can be found here: https://software.intel.com/sites/defaul ... -3abcd.pdf . This includes the complete instruction set architecture reference. The int instruction details all the checks that occur for external, software, and hardware exceptions. That can be found in the section titled INT n/INTO/INT3/INT1—Call to Interrupt Procedure