What’s wrong with this code, part 12 – Retro Bad Code

I was at a security tech talk last week discussing some fascinating stuff, and it reminded me of an interview question that my manager used to give to people who said that they understood x86 Assembly language.

I realized that it would make an interesting "retro" "what's wrong with this code" so, here goes.


When you're writing code for low level platforms (REALLY low level platforms), one of the first things you need to start handling is processor interrupts.  On the x86 family of processors, when an interrupt is generated (either hardware or software), the processor generates a trap frame with the following information (I may have the CS and IP backwards, check your processor manual):

Your code is now executing, but you're running on the application's stack.  So the very first thing that has to happen in your interrupt handler is that you've got to switch to your own stack (you need to do this because you don't know how much memory is remaining on the user stack - if you overflow the user's stack, you've got problems.

So you'd have to write code that switches from the user's stack to your kernel mode stack.

My boss used to use an example that was the system call dispatcher for a theoretical operating system, which maintained a 32bit pointer to the kernel mode stack in a global variable, I'll continue that tradition.

    <code to establish DS>
    MOV    [Saved_SS], SS
    MOV    [Saved_SP], SP
    MOV    SP, WORD PTR [Dos_Stack]
    MOV    SS, WORD PTR [Dos_Stack]+2
    <code to dispatch off of the value in AH>

The problem is that there's a MASSIVE bug in this code.  And it's not because of the use of global variables for the saved SS and SP - assume that the reentrancy issues are handled in the <code to establish DS> section above.

Comments (23)

  1. Anonymous says:

    A nice easy one then.

    Setting SP before SS, if an interrupt happens at this point then SS:SP points into a an effectively random memory and may well clobber the calling processes stack or data segment.

    The sequence should always be SS then SP which disables interrupts until after the next instruction.

  2. Anonymous says:

    Thinking some on this reminds me of when writing some BCD divide and multiply code I snaffled the SS register to allow me to use [di+bp] but have it point to the data segment without having the overhead of the segment override prefixes.

    The code turned off interrupts while I did this and was hand tuned for efficency on a traditional 8086 and is the only code I’ve ever used xlat in. (which meant bx wasn’t available)

  3. ronab49 says:

    it overwrites the stack at Dos_Stack.

  4. Anonymous says:

    Shouldn’t BP also be set to something?

  5. Anonymous says:

    As far as I remember (it was a long time ago 🙂 ) if you use SP or BP the default addressing will be SS:SP or SS:BP. So, if I’m right, you will store the stack pointer in the application stack 🙂

    And, of course, the correct way would be to load SS, and only then SP.

  6. Anonymous says:

    No, I am wrong. We do not use SP in addressing here… Should be more attentive…

  7. Martin Kulov says:

    Well, what about 64bit architectures? If we use WORD PTR to get the value from the memory we will end up with only half of the register’s value.

  8. Since this is 16 bit code, I’m not worried about what happens when it’s run on a 64bit processor…

  9. Anonymous says:

    I think the problem will show up if another interrupt (one with a higher priority) happens between the last two MOV instructions like so:

    MOV SP, WORD PTR [Dos_Stack]

    <interrupt here>

    MOV SS, WORD PTR [Dos_Stack]+2

    In that case the stack would get corrupted.

    Cannot wait for the answers … 🙂

  10. Edge says:

    I agree with some of the others, I believe the loads of SP/SS are reversed. From the Intel docs:

    Loading the SS register with a MOV instruction inhibits all interrupts until after the execution

    of the next instruction. This operation allows a stack pointer to be loaded into the ESP register

    with the next instruction (MOV ESP, stack-pointer value) before an interrupt occurs1. Be aware

    that the LSS instruction offers a more efficient method of loading the SS and ESP registers.


    As SP is loaded first it’s possible for an interrupt to occur before SS is loaded (thus the stack is pointing off into who knows where).

  11. Anonymous says:

    > <code to establish DS>

    Don’t we also need to save DS somewhere, since we’re going to screw it up by “establishing”?

    Me, I’d probably use my code segment for these critical points where I have no stack and no data segment. And I’d also disable interrupts while mucking around with the stack registers, just in case.


    MOV CS:[oldSS], SS

    MOV CS:[oldSP], SP

    MOV SS, CS:[mySS]

    MOV SP, CS:[mySP]

    ; now we have our stack

    ; let’s switch to our data segment as well


    MOV DS, CS:[myDS]


  12. Anonymous says:

    I agree with two of the other posters. Setting SP and SS at different times will cause all sorts of problems. Interupts have a magical habit of finding the worst place to happen.

  13. Anonymous says:

    Centaur, that doesn’t work. Edge got it right.

    The problem is that, even if you use CLI to disable interrupts, there’s still the NMI. That interrupt can still happen even when interrupts are disabled.

    Using "LSS SP,…" loads both: SS and SP in one instruction, so the NMI interrupt will happen before either is set, or after both are set. It’s the whole purpose for the existence of the LSS instruction.

  14. Anonymous says:

    You know what? I’m stoopid. Maybe I should _really_ read the posts, or think a little before jumping the gun.

    I mean… I don’t remember if what Edge says (that after executing "MOV SS,…" the interrupts are disabled for one more instruction) was true. It probably was. Also, I don’t know if that would apply to NMIs.

    But… there’s one more problem. It is reentrancy-related, after all. If an interrupt happens after setting SS and SP, will it use the same stack, thus overwriting the stack of the original interrupt? That can be avoided by using code like this:



    CMP AX,[Dos_Stack_Segment]

    JNE StackChange


    CMP SP,[Dos_Stack_Begin]

    JB StackChange

    CMP SP,[Dos_Stack_End]

    JB NoStackChange


    LSS SP,[Dos_Stack]


    POP AX

    Off the top of my head…

    Basically, if the stack already points to the DOS stack, then don’t change it. You could also have multiple stacks for the different kinds of interrupts, but that’s much more memory-inefficient. As long as your system can somehow limit the amount of interrupt nesting, so that you can size the system stack appropriately, this would work nicely.

  15. Anonymous says:

    Then again, Larry did mention that reentrancy was somehow handled already. Ok, I shut up now.

  16. Anonymous says:

    You have to do the MOV SS before the MOV SP. Otherwise, you can get an interrupt between the MOV SP and the MOV SS, and then you’re running on a stack in a random location in memory. Doing the MOV SS first prevents this, because the processor suppresses interrupts for one instruction after a MOV SS just for this reason.

    Everybody knows that, right? Or am I showing my age…

  17. Anonymous says:

    If it sounds like I didn’t notice all the previous comments, that’s because I didn’t, until afterward… 🙂

  18. Anonymous says:

    I agree, MOV SP before MOV SS is definitely a bug (long time ago I made the same error myself) but what about the interrupt flag?

    I seem to remember interrupts are *disabled* in an interrupt handler (unless an STI has been issued of course).

    So the question of the correct MOV SP/SS order might be moot…

  19. Edge says:

    @ JCAB:

    I don’t think that’s an issue (the re-entrancy ideas you presented). After SS:(E)SP have been set, any future interrupt would use this newly created stack (assuming the first thing it did wasn’t to move to it’s own stack as well). You’ll note that Larry’s interrupt code saves the value of SS:(E)SP and then presumably restores those registers before exiting. If another interrupt took place after you’d set SS:(E)SP that code would likely save your changes and restore them before returning back to your code. (If it didn’t, I’d consider that code to be buggy and outside the scope of this discussion).

    Make sense?

    As far as the issue of setting SS then (E)SP blocking interrupts, the bit I quoted above is from Intel’s Pentium 4 reference manual (and that passage has remained the same for as far back as the 286 I think if not earlier). I do agree that LSS is probably a much better solution for modern code though.

  20. Anonymous says:

    I agree. From the Intel 80386 programmer manual :

    9.2.4 MOV or POP to SS Masks Some Interrupts and Exceptions

    Software that needs to change stack segments often uses a pair of

    instructions; for example:

    MOV SS, AX

    MOV ESP, StackTop

    If an interrupt or exception is processed after SS has been changed but

    before ESP has received the corresponding change, the two parts of the stack

    pointer SS:ESP are inconsistent for the duration of the interrupt handler or

    exception handler.

    To prevent this situation, the 80386, after both a MOV to SS and a POP to

    SS instruction, inhibits NMI, INTR, debug exceptions, and single-step traps

    at the instruction boundary following the instruction that changes SS. Some

    exceptions may still occur; namely, page fault and general protection fault.

    Always use the 80386 LSS instruction, and the problem will not occur.

  21. Universalis says:

    MOVE SS,… only disables interrupts [just for one single instruction after it] on modern processors. Certainly it didn’t on the 8088/8086. The change came in either with the 186 or the 286 – not sure which.

  22. Anonymous says:

    Larry wrote:

    " Since this is 16 bit code, I’m not worried about what happens when it’s run on a 64bit processor…"

    This reminded me of a movie.

    "Be afraid. Be very afraid…"

    If somehow any of my 16-bit x86 code managed to run on any 64-bit CPU, I’d think there are far more serious matters to worry about. F.ex. the fact THAT it did run. 🙂

  23. Anonymous says:

    In the last article, I looked at a prototype code snippet to enter a system call.

    But the code had a…

Skip to main content