Skip to content

config.c KernelAlloc doesn't check that (UMB) memory is sufficient #203

Open
@ecm-pushbx

Description

@ecm-pushbx

I traced a crash that I encountered to the call in

sp = sp->sftt_next = (sfttbl FAR *)
which calls KernelAlloc in
void FAR * KernelAlloc(size_t nBytes, char type, int mode)
which at this point transfers to KernelAllocPara in
void FAR * KernelAllocPara(size_t nPara, char type, char *name, int mode)

The problem is that the latter function never checks that sufficient memory is available in the referenced memory block. In my test case this causes the kernel to overwrite memory not belonging to it, corrupting my little test's int 2Fh handler and likely part of the resident debugger (bootable lDebug) as well.

I'm running in "dosemu2 2.0pre9-dev-20240420-1892-g6a2f4f527 Configured: 2024-04-18 10:57:53 +0000" and booting into a recent lDebug revision. The kernel I use is from the bin/ subdirectory of my most recent build, renamed from kernel.sys to fdkernel.sys, and configured using sys config fdkernel.sys checkdebugger=1. I'm also using testpl.com as a device driver, a build of which I uploaded to our server here. (If you really want I can dig up the exact commands to build it from the lDOS boot repo.)

The full config file used contains:

rem config.sys for DOSEMU + FreeDOS
SWITCHES=/F
DOS=UMB,HIGH
dosdata=umb
lastdrive=Z
files=40
stacks=0
buffers=10
device=c:\testpl.com rc now
device=d:\dosemu\umb.sys
device=d:\dosemu\emufs.sys
device=d:\dosemu\ems.sys
device=d:\dosemu\cdrom.sys
install=d:\dosemu\emufs.com
shell=C:\command.com /e:4096 /p:d:\autoexec.bat
;set ldebugconfig=c:\testdir
;device=c:\bin\ldebug.com /c=d68:0;qcd
;device=c:\bin\ldebug.com /c=qdc

The fred327 alias used in the debugger session is defined as follows:

alias add fred327 boot protocol freedos cmdline=1 ldp/fdkernel.sys . config fdconfig.327

(This adds an override to run a different config file than default.)

The testumb1.sld Script for lDebug file used contains:


r word [0:413] -= 2
r v0 = word [0:413]
r v1 = v0 + 1
r v0 *= #1024 / #16
r v1 *= #1024 / #16

f v0:0 l #1024 26
f v1:0 l #1024 CC
f v1:0 l 10 0
a v1:10
 cmp ax, 4300
 je 20
 cmp ax, 4310
 je 30
 jmp 0:0
 .
r v2 = aao - 4
a v1:20
 mov al, 80
 iret
 .
a v1:30
 push cs
 pop es
 mov bx, 40
 iret
 .
a v1:40
 jmp short 45
 nop
 nop
 nop
 cmp ah, 10
 je 50
 .
r v3 = aao
a
 xor ax, ax		; error
 mov bl, 80		; not implemented
 retf
 .
a v1:50
 cmp byte [cs:10], EB
 je 80
 cmp dx, (#1024 / #16 - v4)
 ja 70
 jb (v3)		; if requested smaller -->
 mov ax, 1		; success
 mov bx, (v0 + v4)	; => UMB, dx = size
 mov word [cs:10], (EB + (v2 - 1 - 12) * 100)
 retf
 .
a v1:70
 xor ax, ax		; error
 mov bl, B0		; only a smaller UMB
 mov dx, (#1024 / #16 - v4)
 retf
 .
a v1:80
 xor ax, ax		; error
 mov bl, B1		; no UMB available
 xor dx, dx		; largest available
 retf
 .
rc.replace r vf = ri2Fp; r dword [v1:v2] = vf; r dword [0:2F * 4] = v1*10000 + 10

This script reserves 2 KiB at the top of the Low Memory Area (below the resident debugger and possibly EBDA), using the upper 1 KiB for code space and the lower 1 KiB as an improvised "UMB" space. The v4 variable indicates how much of a gap (in paragraphs) to leave between the top of the LMA and the UMB. I set this to 1 in the test to avoid any possible confounding factors with UMBs that extend the LMA. (This is the original purpose of the test, which showed me that lRxDOS/lMS-DOS incorrectly treat that case.)

The code is an int 2Fh handler which offers an XMS entrypoint. The only supported function is function 10h, allocate UMB, and then only the size query (eg with dx = 0FFFFh) and exact size allocation are supported. After the UMB is allocated, the int 2Fh handler is changed to chain to its downlink immediately (hiding the XMS entrypoint), and subsequent function 10h calls to the XMS entrypoint say there is no free UMB left.

The use of this script is as follows, once booted into lDebug:

r v4 1
y testumb1.sld
fred327
g
r f CY
g
rc

The Y command runs a Script for lDebug file, which is read using int 13h when booted into the debugger. The fred327 alias was listed above. The first g command runs up to the debugger check, which had to be enabled using sys config as above. r f CY indicates debugger present to the kernel.

The second g command runs up to the next breakpoint, which in this FreeDOS kernel test case is the int3 instruction in testpl.com. The rc command hooks int 2Fh to our handler then. That makes it ready for action, to offer its UMB to the config code after the testpl.com device driver returns. Running a third g command would crash dosemu2, due to the kernel corrupting int 2Fh presumably.

I will dump a longer session output as a subsequent comment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions