You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
MCB corruption from mismatch in maximum size allocated for and read from flat-format .COM executable #207
Here's the Script for lDebug file that reliably causes the kernel to corrupt one of its MCBs (if an UMCB chain is present and UMB link state is disabled): https://pushbx.org/ecm/test/20250603/testbigc.sld (I will copy the contents into a subsequent comment in this thread.)
You run it like so:
ldebug
y testbigc.sld
g
dm
l
dm
q
What it does is it exposes a difference in the flat-fomat .COM style executable maximum size to allocate and to load, in task.c. The maximum required memory block is smaller than the maximum file data read, so with a certain range of free MCB size and executable size the allocation will be smaller than the amount of file data read, leading to corrupting memory beyond the end of the allocated memory block (most obviously corrupting the next MCB).
Flat-format executables sized >= FF00h bytes are rejected by the MS-DOS v4.01 and lDOS executable loaders. So the problem might be restricted to pathological cases of executable sizes. However, the other condition of this (precise free block size) is not purely pathological, it can occur when loading to UMBs using LOADHIGH.
Here's where the file size is determined to get the minimum size of memory allocation needed to run the program, it requires at most FE0h + 10h = FF0h paragraphs:
Here's where the file is read, it reads up to FF00h bytes during exec (not overlay/device load) to offset 0100h hence writing to offsets 0100h .. FFFFh:
if (v3 != 0) then y :freev3
if (v4 == 0) then r v4 := 0FF1
if (v5 == 0) then r v5 := FF00
k testbigc.com
f 100 FFFF CC
a 100
retn
.
r bxcx := v5
w 100
r byte [cs:ip + 1] := -1 ; workaround
l
if (rc) then goto :eof ; this doesn't work really
if (byte [cs:ip + 1] != CC) then goto :eof ; workaround
f 100 l 400 CC
a 100
mov ah, 4A
mov bx, 1000
int 21
xor ax, ax
xchg ax, [2C]
dec ax
mov es, ax
mov bx, word [es:3]
inc ax
mov es, ax
mov ah, 49
int 21
add bx, (v4)
mov ah, 48
int 21
jc 180
push ax
.
r v0 := aao
a
mov ah, 48
mov bx, -1
int 21
mov ah, 48
int 21
jnc (v0)
pop es
mov ah, 49
int 21
xor bx, bx
.
r v1 := aao
a
mov ah, 3E
int 21
inc bx
cmp bx, 20
jb (v1)
mov ax, 3100
mov dx, 1000
int 21
.
r v3 := psp
goto :eof
:freev3
. push es
. push bx
. push ax
. mov ah, 52
. int 21
r v10 := word [es:bx - 2]
. pop ax
. pop bx
. pop es
r v11 := 0
:freeloop
if (! value byte [$v10:0] in #'M', #'Z') then goto :freeerror
if (word [$v10:1] == v3) then r word [$v10:1] := 0
if (byte [$v10:0] == #'Z') then goto :freeend
r v10 += word [$v10:3] + 1
r v11 += 1
if (v11 > FFFF) then goto :freeerror
goto :freeloop
:freeerror
; Error during MCB freeing!
goto :eof
:freeend
; Freed
r v3 := 0
Flat-format executables sized >= FF00h bytes are rejected by the MS-DOS v4.01 and lDOS executable loaders. So the problem might be restricted to pathological cases of executable sizes. However, the other condition of this (precise free block size) is not purely pathological, it can occur when loading to UMBs using LOADHIGH.
Using the value r v5 := FEFF will make the test succeed to load the executable the first time on both FreeDOS and lDOS kernels. It still ends up with FreeDOS corrupting its trailing MCB when continuing the test. So it isn't entirely pathological: The largest valid flat-format .COM executable for MS-DOS v4.01 can elicit this bug from the FreeDOS kernel.
The largest valid flat-format .COM executable for MS-DOS v4.01 can elicit this bug from the FreeDOS kernel.
This appears to be because the overlapping memory areas are larger than 1 paragraph. Here's another test run with r v5 := FEFF depicting how far the memory corruption reaches:
Program terminated normally (0300)
-dm
PSP: 3976
02B9 4D 0008 000D 208 B SD
02C7 4D 2975 000F 240 B TESTBIGC
02D7 4D 02D8 269C 154 KiB LDEBUG
2974 4D 2975 1000 64.0 KiB TESTBIGC
3975 4D 3976 1000 64.0 KiB DEBUGGEE
4976 5A 2975 5688 346 KiB TESTBIGC
9FFF 4D 0008 1000 64.0 KiB SC
B000 4D C151 0100 4.0 KiB COMMAND
B101 4D B102 0019 400 B SEEKEXT
B11B 4D 0000 06E3 27.5 KiB
B7FF 4D 0008 0800 32.0 KiB SC
C000 4D 0008 014F 5.2 KiB SD
C150 4D C151 00A9 2.6 KiB COMMAND
C1FA 4D 0000 1E04 120 KiB
DFFF 4D 0008 1000 64.0 KiB SC
F000 4D 0000 096E 37.7 KiB
F96F 5A 0000 0090 2.2 KiB
-d 4976:0
4976:0000 5A 75 29 88 56 00 00 00-00 00 00 00 00 00 00 00 Zu).V...........
4976:0010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0030 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0060 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0070 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-l
-d 4976:0
4976:0000 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0010 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0020 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0030 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0040 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0050 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0060 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0070 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
-dm
PSP: 3986
02B9 4D 0008 000D 208 B SD
02C7 4D 2975 000F 240 B TESTBIGC
02D7 4D 02D8 269C 154 KiB LDEBUG
2974 4D 2975 1000 64.0 KiB TESTBIGC
3975 4D 3986 000F 240 B TESTBIGC
3985 4D 3986 0FF0 63.7 KiB TESTBIGC
-h 3986+FF0
4976 decimal: 18806
-d 4976:0
4976:0000 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0010 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0020 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0030 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0040 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0050 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0060 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0070 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
-
4976:0080 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:0090 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:00A0 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:00B0 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:00C0 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:00D0 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:00E0 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC CC ................
4976:00F0 CC CC CC CC CC CC CC CC-CC CC CC CC CC CC CC 00 ................
-
4976:0100 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
4976:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-
I think that this should also round up the file size using a + 15 byte displacement somewhere in there. I feel like this can be exploited to cause MCB corruption as well, but I'm too lazy to create that test case right now:
I think that this should also round up the file size using a + 15 byte displacement somewhere in there. I feel like this can be exploited to cause MCB corruption as well, but I'm too lazy to create that test case right now:
That is exactly the code position I also questioned a long time ago if there should be a +15 added to com_size_long, as it currently rounds DOWN by the shift. Too bad I do not find where I posted this...
Reading 0xff00U bytes needs an allocation of 0x10000 bytes including the PSP. But the code allocates 0xff00U bytes at max. I guess therefore 256 bytes will be overwritten.
I changed the kernel source accordingly. I do not get a MCB chain corruption anymore. But under lDebug the l command fails with an out-of-memory error after the generated code of the testbigc.sld was run via g. Is this expected behaviour (still digging through the .sld file)?
Reading 0xff00U bytes needs an allocation of 0x10000 bytes including the PSP. But the code allocates 0xff00U bytes at max. I guess therefore 256 bytes will be overwritten.
Indeed.
I changed the kernel source accordingly. I do not get a MCB chain corruption anymore. But under lDebug the l command fails with an out-of-memory error after the generated code of the testbigc.sld was run via g. Is this expected behaviour (still digging through the .sld file)?
Activity
ecm-pushbx commentedon Jun 3, 2025
testbigc.sld file contents:
ecm-pushbx commentedon Jun 3, 2025
Test log:
ecm-pushbx commentedon Jun 3, 2025
Using the value
r v5 := FEFFwill make the test succeed to load the executable the first time on both FreeDOS and lDOS kernels. It still ends up with FreeDOS corrupting its trailing MCB when continuing the test. So it isn't entirely pathological: The largest valid flat-format .COM executable for MS-DOS v4.01 can elicit this bug from the FreeDOS kernel.ecm-pushbx commentedon Jun 3, 2025
This appears to be because the overlapping memory areas are larger than 1 paragraph. Here's another test run with
r v5 := FEFFdepicting how far the memory corruption reaches:ecm-pushbx commentedon Jun 3, 2025
I think that this should also round up the file size using a
+ 15byte displacement somewhere in there. I feel like this can be exploited to cause MCB corruption as well, but I'm too lazy to create that test case right now:kernel/kernel/task.c
Lines 470 to 473 in b8ef68f
boeckmann commentedon Jun 3, 2025
That is exactly the code position I also questioned a long time ago if there should be a +15 added to com_size_long, as it currently rounds DOWN by the shift. Too bad I do not find where I posted this...
boeckmann commentedon Jun 3, 2025
Found it :) #195 (comment)
boeckmann commentedon Jun 4, 2025
Reading 0xff00U bytes needs an allocation of 0x10000 bytes including the PSP. But the code allocates 0xff00U bytes at max. I guess therefore 256 bytes will be overwritten.
I changed the kernel source accordingly. I do not get a MCB chain corruption anymore. But under lDebug the
lcommand fails with an out-of-memory error after the generated code of the testbigc.sld was run viag. Is this expected behaviour (still digging through the .sld file)?boeckmann commentedon Jun 4, 2025
This is what I changed in task.c:
ecm-pushbx commentedon Jun 4, 2025
Indeed.
Yes, it is.
ecm-pushbx commentedon Dec 5, 2025
No fix seems to have been committed yet. I may study your patch sometime again.
ecm-pushbx commentedon Dec 5, 2025
Regarding this bit of the Script for lDebug presented here, I just added a changeset that fixes the RC setting so that the L command will set the RC variable on errors. This will be included in today's build that is going to be available at https://pushbx.org/ecm/download/old/ldebug/20251205.zip