dosemu2 / fdpp

FreeDOS plus-plus, 64bit DOS
GNU General Public License v3.0
195 stars 17 forks source link

int20 should take PSP from CS #185

Open stsp opened 2 years ago

stsp commented 2 years ago

Currently int20 terminates current PSP instead of the one in CS. Hope no one cares, this is a minor bug. Same with freedos.

stsp commented 2 years ago

Same for int27.

andrewbird commented 2 years ago

Is this something we should test across all the DOS variants? Did you find out with a testcase or by studying the code?

stsp commented 2 years ago

By studying the code. int20_handler() and int27_handler() just pass to int21. Test would be cool.

andrewbird commented 2 years ago

Mmm, isn't int27 a strange one. Thinking about how to test this, I figure I have to do the following

1/ resize (int21/4a) myself to leave space for a new allocation 2/ allocate a new some space(int21/48) 3/ build a new PSP there. (what's the minimum requirement?) 4/ copy in some minimal code to run 5/ far jump to it 6/ TSR (int27)

But I wonder how to test from DOS afterwards which program is resident, and what was released?

stsp commented 2 years ago

I think the easiest is to have a com file. First, it loads itself via 4b one but sets the "no execute" mode. But it will create&change PSP. Then you get PSP with 62h and compare it with CS:

AX=4b01;
do_int21();
if (PSP == CS) {
    printf("Test FAILED\n");
    exit(1);
} else {
    /* we are on child PSP now */
    do_int20();
    /* no return */
}

This is all. So if PSP is child's, we execute int20. If it ignored the current PSP and looked up CS, then we immediately exit to command.com with 0 exit code. This should be a correct behavior (but the child's com file will be leaked - maybe you can improve the test to avoid mem leak). But if it instead terminated child's PSP, then we return from do_int21() for the second time and PSP now matches CS, which we detect.

stsp commented 2 years ago

Even simpler is to just use the static variable to count the number of times you returned from do_int21():

AX=4b01;
do_int21();
if (cnt++ == 0)
    do_int20();
else
    printf("Test FAILED\n");
andrewbird commented 2 years ago

No sure I get it fully yet, but I'll experiment with it later when I return home. Thanks for the ideas!

ecm-pushbx commented 2 years ago

Interrupt 21h function 00h has the same behaviour as int 20h in MS-DOS, I believe. (You can look at the MIT-licensed MS-DOS version 2 sources.)

stsp commented 2 years ago

Yes, good point. Indeed freedosish asm replaces int20 with int21/00, and later the C code does another morphing:

      /* Terminate Program                                            */
    case 0x00:
      lr.AX = 0x4c00;

      /* End Program                                                  */
    case 0x4c:

Unfortunately, checking CS breaks the progs under debugger. For that reason we already stopped checking CS in int21/26h. I suppose we should stop adding an extra stack frames with the debugger and just use the breakpoint instead. I believe Andrew worked on something like that already?

andrewbird commented 2 years ago

I believe Andrew worked on something like that already?

Yes I just found that old local branch, amusingly the final commit in the series was called 'crap commit - about to give up`. So there's probably nothing of value there! :smile:

tkchia commented 2 years ago

Hello @stsp, @ecm-pushbx, @andrewbird,

Arrrgh. My understanding is that both int 0x20 and int 0x21 function 0x00 kind of assume that cs points to the current PSP. int 0x21 function 0x4c does not, though, which is why it can be called from anywhere in conventional memory.

Are there actually any real-life programs that try to pull a fast one, and invoke int 0x20 (or int 0x21, ah = 0) with cs ≠ current PSP? :thinking:

stsp commented 2 years ago

Hope there is none. Which is why fixing this "bug" is not very tempting. :) But certainly a test can be written that reveals an "incompatibility".

tkchia commented 2 years ago

Hello @stsp,

Which is why fixing this "bug" is not very tempting. :)

Me, I have half a mind to make the kernel complain "lolwut, your program is doing something bogus!" when some program calls int 0x20 with cs ≠ PSP...

Thank you!

stsp commented 2 years ago

This can't work because int21 can be hooked. In that case you may see CS of a hooker on stack. Also debugger may put an additional iret frame... Overall checking CS is a PITA, and especially not worth a trouble just as a sanity check. Right now fdpp simply uses current psp, and that is obviously the Right Thing (tm), except that MS-DOS does otherwise.