Modifying luac to build and run as a ps-exe - #4
Conversation
rixnobis
left a comment
There was a problem hiding this comment.
The approach is right and the PCDRV plumbing is clean. Reading argv out of an unmapped-memory hook is a good trick. One blocker, then a list.
Blocker - stack buffer overflow in main().
const char * argv[64] = { 0 };
...
while (1) {
int len = luaA_strlen(argsPtr);
if (len == 0) break;
argv[argc++] = argsPtr; // no bound on argc
argsPtr += len + 1;
}65 strings in the buffer and this writes past argv. It is fully reachable: createArgsBuffer(...) takes varargs straight from the console, so createArgsBuffer(unpack(t)) with a 65-element table smashes the stack. Needs if (argc == 64) break; at minimum. A truncation the caller never hears about is its own bug, so say something.
The same loop has no bound on the scan. If args.lua is not loaded, nothing guarantees a NUL ever appears at 0x40000000 and it walks until it faults. A total-length cap costs one comparison.
args.lua reads one byte past the end.
if offset > ffi.sizeof(argsBuffer) then return 0xff end
return argsBuffer[offset]Valid indices are 0 .. sizeof-1, so offset == sizeof passes the guard and indexes off the end. >=.
Also spaceNeeded = spaceNeeded + #a + 1 in the second loop is dead. The size is fixed and the buffer allocated by the time it runs.
psx-glue.s builds an argv that nothing reads. main is now int main() and takes its arguments from 0x40000000, so li $a0, 1 / la $a1, _mainargv and the whole _mainargv/_progname rodata block are unreachable. That is also why the two files disagree about the program name - "PSX.EXE" in the assembly against 'luac.ps-exe' in the Lua - and the assembly one can never win. Either delete it, or give main its (argc, argv) back and let the glue supply the fallback for the no-hook case.
Two standard-library names squatted on.
#define EOF -1
static int getc(int f) { ... }getc is a reserved identifier, and in hosted C it is a macro. EOF is a standard macro too. This compiles because stdio.h is gone; it breaks the moment anything pulls one in. LUAC_EOF and luaA_getc, and parenthesise the -1.
writer() compares signed to unsigned. int r against size_t size: r != size promotes r, so a PCwrite return of -1 compares as a huge unsigned and happens to give the right answer. r < 0 || (size_t)r != size.
Dead branch. doargs no longer handles -, so a bare - now falls to usage(). That makes IS("-") in pmain unreachable.
The reformat. +446/-445, and git diff -w --ignore-blank-lines says +211/-209 - so about half of this is reindentation of a vendored upstream file. psxlua's value is being Lua 5.2.4 with a delta somebody can read; this doubles the delta for no behaviour. Split it, or drop it. I do not mind which, and I do mind that they are in one commit.
Happy to take the fixes myself if you would rather hand it over - you said there was building and documentation left anyway.
rixnobis
left a comment
There was a problem hiding this comment.
The approach is right and the PCDRV plumbing is clean. Reading argv out of an unmapped-memory hook is a good trick. One blocker, then a list.
Blocker - stack buffer overflow in main().
const char * argv[64] = { 0 };
...
while (1) {
int len = luaA_strlen(argsPtr);
if (len == 0) break;
argv[argc++] = argsPtr; // no bound on argc
argsPtr += len + 1;
}65 strings in the buffer and this writes past argv. It is fully reachable: createArgsBuffer(...) takes varargs straight from the console, so createArgsBuffer(unpack(t)) with a 65-element table smashes the stack. Needs if (argc == 64) break; at minimum. A truncation the caller never hears about is its own bug, so say something.
The same loop has no bound on the scan. If args.lua is not loaded, nothing guarantees a NUL ever appears at 0x40000000 and it walks until it faults. A total-length cap costs one comparison.
args.lua reads one byte past the end.
if offset > ffi.sizeof(argsBuffer) then return 0xff end
return argsBuffer[offset]Valid indices are 0 .. sizeof-1, so offset == sizeof passes the guard and indexes off the end. >=.
Also spaceNeeded = spaceNeeded + #a + 1 in the second loop is dead. The size is fixed and the buffer allocated by the time it runs.
psx-glue.s builds an argv that nothing reads. main is now int main() and takes its arguments from 0x40000000, so li $a0, 1 / la $a1, _mainargv and the whole _mainargv/_progname rodata block are unreachable. That is also why the two files disagree about the program name - "PSX.EXE" in the assembly against 'luac.ps-exe' in the Lua - and the assembly one can never win. Either delete it, or give main its (argc, argv) back and let the glue supply the fallback for the no-hook case.
Two standard-library names squatted on.
#define EOF -1
static int getc(int f) { ... }getc is a reserved identifier, and in hosted C it is a macro. EOF is a standard macro too. This compiles because stdio.h is gone; it breaks the moment anything pulls one in. LUAC_EOF and luaA_getc, and parenthesise the -1.
writer() compares signed to unsigned. int r against size_t size: r != size promotes r, so a PCwrite return of -1 compares as a huge unsigned and happens to give the right answer. r < 0 || (size_t)r != size.
Dead branch. doargs no longer handles -, so a bare - now falls to usage(). That makes IS("-") in pmain unreachable.
The reformat. +446/-445, and git diff -w --ignore-blank-lines says +211/-209 - so about half of this is reindentation of a vendored upstream file. psxlua's value is being Lua 5.2.4 with a delta somebody can read; this doubles the delta for no behaviour. Split it, or drop it. I do not mind which, and I do mind that they are in one commit.
Happy to take the fixes myself if you would rather hand it over - you said there was building and documentation left anyway.
Duplicate - I fired gh pr review twice. The identical review below it is the one to read.
|
Regarding the argv trick: not having the args.lua loaded would crash immediately anyway. The 0x40000000 address is otherwise unmapped, and would trigger a violation on the first read, which is the whole point of this Lua hook. Feel free to do the changes yourself, yes :) Maintainer edits have been enabled. |
Arguments as passed through pcsx-redux' debugging ability to hook into invalid memory reads, through the args.lua script, which needs to be loaded at the same time as running the luac.ps-exe binary.
argv[] was filled without a bound, getc/EOF squatted on reserved names, writer() compared signed to unsigned, args.lua read one past the end, and the glue built an argv main() no longer takes.
26f1622 to
d77fa02
Compare
|
The scan point was wrong. 0x40000000 faults on the first read without args.lua, so the loop can't run past the end. Dropped it. Pushed the rest:
Compile-checked with I took the reformat split too, which means I rewrote the branch - Build wiring and docs are still yours. |
Addressed on the branch. What's left is the build wiring, which isn't a review item.
|
Hah, are you making me do the remaining items instead of doing them yourself? :) |
NUGGET points at a nugget checkout rather than vendoring one, since nugget's psyqo-lua consumes psxlua and a submodule here would close the cycle. psx-heap.c supplies the allocator psxlua leaves to its embedder, over the kernel heap.
|
Your nugget note arrived after I'd added it as a submodule. Backed that out - Empty, it fails at that line with the variable named.
Links clean: 318 defined symbols, zero undefined, entry 0x80010000, And one I should have caught in the first pass: psxlua's bytecode is not stock 5.2.4 bytecode - |
The ps-exe compiler is the one whose bytecode is a sure thing; a matching 32-bit host build is not available in most environments any more.
|
Then I had it backwards. Taken.
The README also covers
One discrepancy worth your eye: the README lists |
…on the console. tests/run-luac.sh drives luac.ps-exe under pcsx-redux and checks the bytecode header is the target's - Lua 5.2, little endian, 4-byte int, size_t, Instruction and Number, integral. The emulator comes from the appdistrib dev channel.
pcsx-redux initialises SDL video even in -testmode, so a bare runner has no video device.
A run that never reaches pcsx_exit held a CI job open for six minutes before it was cancelled.
Arguments as passed through pcsx-redux' debugging ability to hook into invalid memory reads, through the args.lua script, which needs to be loaded at the same time as running the luac.ps-exe binary.
What's left to do: building the binary, maybe through xmake, using the xmake-psx project, and some documentation on how to run the compiler through pcsx-redux.