-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FEXLoader: Adds support for execveat with AT_EMPTY_PATH #2334
Conversation
82a2b36
to
c74bf13
Compare
c74bf13
to
a958f36
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic here seems very intricate. I've done a first pass trying to wrap my head around it, adding a few comments with minor performance improvements and suggestions to clarify some documentation.
File.seekg(0, File.end); | ||
FileSize = File.tellg(); | ||
File.seekg(0, File.beg); | ||
|
||
// Is the file large enough for shebang | ||
if (FileSize <= 2) | ||
return false; | ||
|
||
// The maximum length of the shebang line is `#!` + 255 chars | ||
std::vector<char> Header(std::min(257ul, FileSize)); | ||
File.read(&Header.at(0), Header.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeking twice to perform the pathological size check is probably (?) more expensive than always reading and checking later, so:
std::array<char, 257> Header;
auto ChunkSize = File.read(Header.data(), Header.size());
(where the size check now happens in IsShebangFile
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the logic with this comment and others conflicted. So you'll need to check that again with the changes.
if (strcmp(pathname, "/proc/self/exe") == 0 || | ||
strcmp(pathname, "/proc/thread-self/exe") == 0 || | ||
strcmp(pathname, PidSelfPath) == 0) { | ||
// If pointing to self then redirect to the application |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly is the scenario described here? Is it "FEX runs java
, and java
then calls execve
on a symlink to its own executable to start the guest Java application"?
At that point, what is /proc/self/exe
actually pointing to - FEX or java
? Comment needs to be a bit clearer, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated comment.
7c68474
to
9bd0ecb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second pass done: I mostly understand what's happening now. The added documentation really helped!
We're getting there :)
Source/Common/Config.cpp
Outdated
// - Full application path with execve (See Side Note) | ||
// - FD path with execveat and FD doesn't have an existing filesystem on the disk | ||
// | ||
// ProgramFDFromEnv: Arg[0] is set to Application provided data or `/dev/fd/<FD>` from above fix-up. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is useful information, but it mixes "things that applications do" with "how they do it" and (maybe?) "how FEX handles" it. Currently it's difficult to tell which of these you're describing at which point.
If you're describing what values certain values take, I'd use "the value of X will be" over "X is set", since the latter is ambiguous about who sets it (FEX before this line? FEX after this line? The application? ...).
If you're describing values that we actively set after the comment, active phrasing ("We set"/"FEX sets") helps clarify this over passive phrasing ("is set").
FDExecEnv = "FEX_EXECVEFD=" + std::to_string(Args.dirfd); | ||
|
||
// Insert the FD for FEX to track. | ||
EnvpArgs.emplace_back(FDExecEnv.data()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this extra argument actually used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in the next FEXInterpreter instance that is getting executed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where can I see this in the code? Do we actively have to extract it again from envp on the other end, or does this happen indirectly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/FEX-Emu/FEX/pull/2334/files#diff-cd1944a762a8c051124ab5532528cddff1933f5c0d2e3a45785c36751868b244R224
We extract it from envp here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes - I was somehow looking for a manual envp
readout as opposed to getenv
. Makes sense!
7e59e6c
to
42c435c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Fixes FEX-Emu#2136 This is a fairly tricky edge case to support with FEX. If execveat is used with AT_EMPTY_PATH then the application can pass an FD to execve instead of a filename. This includes FDs that have been deleted from the disk so the child process can't open it by filename anymore. To work around this limitation, we need to pass the FD to the new FEX process and open it directly, similar to how binfmt_misc works with FDs. The FD will get passed through environment variables, which the new process will check for and then remove the variable from the environment. Lots of prickly edge cases to support here. Without binfmt_misc: - Passes the FD to FEXLoader directly. - Requires duplicating the FD if it has O_CLOEXEC on the FD. With binfmt_misc: - Shebang file, pass directly to FEXLoader, just like without binfmt. - x86 ELF Files, rely on the kernel's binfmt_misc support here. - Unsupported ELF files, let kernel handle it through binfmt_misc Argument handling: - The application can pass in no arguments. - Means our application configurations were failing to find a config - Also various checks in the frontend were failing. - If opened through an FD, find the symlink for that FD for the application configuration instead. Side note: Fixed a performance issue in execve where when we were checking for file format support. Either ELF or Shebang files, we were reading the /whole/ file upfront. We only need to read a header worth of ELF files, and only 257 bytes if it is potentially a shebang file. Should dramatically reduce some application's execve times.
42c435c
to
472675d
Compare
Fixes #2136
This is a fairly tricky edge case to support with FEX.
If execveat is used with AT_EMPTY_PATH then the application can pass an
FD to execve instead of a filename. This includes FDs that have been
deleted from the disk so the child process can't open it by filename
anymore.
To work around this limitation, we need to pass the FD to the new FEX
process and open it directly, similar to how binfmt_misc works with FDs.
The FD will get passed through environment variables, which the new
process will check for and then remove the variable from the
environment.
Lots of prickly edge cases to support here.
Without binfmt_misc:
With binfmt_misc:
Argument handling:
application configuration instead.
Side note:
Fixed a performance issue in execve where when we were checking for file
format support. Either ELF or Shebang files, we were reading the /whole/
file upfront. We only need to read a header worth of ELF files, and only
257 bytes if it is potentially a shebang file. Should dramatically
reduce some application's execve times.