This is racy in userspace and non-racy in kernelspace so let's keep
it in kernelspace.
The behavior change where CLOEXEC is preserved when dup2() is called
with (old_fd == new_fd) was good though, let's keep that.
This enables a nice warning in case a function becomes dead code. Also, in case
of signal_trampoline_dummy, marking it external (non-static) prevents it from
being 'optimized away', which would lead to surprising and weird linker errors.
When compiling with "-Os", GCC produces the following pattern for
atomic decrement (which is used by our RefCounted template):
or eax, -1
lock xadd [destination], eax
Since or-ing with -1 will always produce the same output (-1), we can
mark the result of these operations as initialized. This stops us from
complaining about false positives when running the shell in UE. :^)
The emulator will now register signal handlers for all possible signals
and act as a translation layer between the kernel and the emulated
process.
To get an accurate simulation of signal handling, we duplicate the same
trampoline mechanism used by the kernel's signal delivery system, and
also use the "sigreturn" syscall to return from a signal handler.
Signal masking is not fully implemented yet, but this is pretty cool!
We don't have to be clever at all to figure out which MmapRegions are
malloc blocks, we can just mark the containing region as such when
the emulated process performs a malloc! :^)
Some of the remaining instructions have different behavior for
register and non-register ops. Since we already have the
two-level flags tables, model this by setting all handlers in
the two-level table to the register op handler, while the
first-level flags table stores the action for the non-reg handler.
Some of these don't just use the REG bits of the mod/rm byte
as slashes, but also the R/M bits to have up to 9 different
instructions per opcode/slash combination (1 opcode requires
that MOD is != 11, the other 8 have MODE == 11).
This is done by making the slashes table two levels deep for
these cases.
Some of this is cosmetic (e.g "FST st0" has no effect already,
but its bit pattern gets disassembled as "FNOP"), but for
most uses it isn't.
FSTENV and FSTCW have an extraordinary 0x9b prefix. This is
not yet handled in this patch.
This virtual syscall works by exec'ing the UserspaceEmulator itself,
with the emulated program's provided arguments as the arguments to the
new UserspaceEmulator instance.
This means that we "follow" exec'ed programs and emulate them as well.
In the future we might want to make this an opt-in (or opt-out, idk)
behavior, but for now it's what we do.
This is really quite cool, I think! :^)
Note that running a setuid program (e.g /bin/ping) in UE does not
actually run uid=0. You'll have to run UE itself as uid=0 if you want
to test programs that do setuid/setgid.
"xor reg,reg" or "sub reg,reg" both zero out the register, which means
we know for sure the result is 0. So mark the value as initialized,
and make sure we don't taint the CPU flags.
This removes some false positives from the uninitialized memory use
detection mechanism.
Fixes#2850.
Instead of using SoftCPU::eip() which points at the *next* instruction
most of the time, stash away a "base EIP" so we can use it when making
backtraces. This makes the correct line number show up! :^)
We now track whether the flags register is tainted by the use of one or
more uninitialized values in a computation.
For now, the state is binary; the flags are either tainted or not.
We could be more precise about this and only taint the specific flags
that get updated by each instruction, but I think this will already get
us 99% of the results we want. :^)