-
Notifications
You must be signed in to change notification settings - Fork 0
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
Implement injection of a forkserver into the compiler process #3
base: main
Are you sure you want to change the base?
Conversation
On review request: for now, SCM code base is quite sloppy: it has inconsistent Right now, I would rather discuss potential security implications of such an approach. First, I thought communicating with forkserver via UDP, but this would be quite hard to restrict to a single user, etc. Now it seems I have a single but quite significant questionable part: compiling and injecting .so into current user's process. This should probably be secured via proper handling of temporary files, so it's the main question to be reviewed. |
5761994
to
86b9850
Compare
Hmm... Looks like standard |
c55b59b
to
c78890b
Compare
Implement a forkserver inspired by AFL fuzzer. Unlike the original design, this one relies on library preloading and seccomp to eliminate static or dynamic recompilation of the compiler. For single-threaded, single-process compilers this can be several times faster, provided that the interacting with any input files is delayed until absolutely necessary. For example, it is the case when the compiler process first loads the standard library for several seconds and then processes scratch files in several milliseconds.
c78890b
to
63cb1fd
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.
Sorry for the late review.
for now, SCM code base is quite sloppy: it has inconsistent throws specifications, logging to
stdout/stderr, etc. But I would rather delay it more towards 1.0 release for better understand required
public APIs.
That's fine. As for logging, I would consider using slf4j rather than java.util.logging since it provides more flexibility later on.
Now it seems I have a single but quite significant questionable part: compiling and injecting .so into
current user's process. This should probably be secured via proper handling of temporary files, so
it's the main question to be reviewed.
Do you have any specific attack vector in mind?
We are injecting the .so into the compiler process (or whatever thing we are supposed to be executing), aren't we? (so we are not modifying the current user's process, which is java/pmd-scm).
Are you concerned, that the .so could be manipulated before it is used (and after we have compiled it)?
For now, I would say, the file permissions are ok, so that this scenario is prevented.
The attacker could however potentially manipulate any other input file, which could in theory trigger unexpected actions - I mean input files used for executing pmd-scm. But I guess, that's almost always the possibility and could be prevented by using proper file permissions/umask settings.
/** | ||
* The size of buffer for a single output line read by this class. | ||
*/ | ||
private final static int MAX_LINE_LENGTH = 65536; |
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.
I think, this is not needed anymore, since you are using now BufferedReader (which probably uses the same value by default).
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.
Oops, forget to remove, thanks!
*/ | ||
private Path compilePreloadedObject() throws IOException { | ||
try { | ||
Path input = Files.createTempFile("pmd-scm-forkserver-", ".c"); |
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.
The generated temp filename has a random number in it, so the attacker would need to guess the name, if he wants to inject his own code.
The created temp file is in the standard temp directory. By default, it is created with only the user permissions, so no one else can read/modify it.
Path output = Files.createTempFile("pmd-scm-forkserver-", ".so"); | ||
deleteAtExit.add(output); | ||
Files.copy(getClass().getResourceAsStream("forksrv-preload.c"), input, StandardCopyOption.REPLACE_EXISTING); | ||
String compiler = System.getenv("CC"); |
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.
Depending on what access the attacker already has, he could provide a different program as "cc", that we would use then without knowing. We simply execute "cc" (or whatever env CC points to).
.start(); | ||
int exitCode = compilerProcess.waitFor(); | ||
if (exitCode != 0) { | ||
throw new IOException("Cannot compile forkserver preloaded object: compiler exited with code " + exitCode); |
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.
Technically this is not a IOException - something's wrong with the compiler or the source code. But that's a detail (like the logging vs. system.err)
Thank you for review!
Yes, we are injecting the .so into spawned compiler process, not into the JVM.
Yes, I am primarily concerned about an attacker (on a multi-user system) being able to manipulate either the compiled .so OR unpacked .c source. I expect an attacker being able to monitor the created file names in, say,
Hmm... I even not considered this yet :) But I suppose, that since input/output file names are user-controlled anyway, I don't have to impose some security restrictions on them. I consider this user's environment safe as well ( So, my concerns are not about creating obscure security measures in an already compromised account but just about not creating new threats (such as a possibility for other local user to inject some code to be executed by us because of improper API handling in SCM). |
Implement a forkserver inspired by AFL fuzzer. Unlike the original
design, this one relies on library preloading and seccomp to
eliminate static or dynamic recompilation of the compiler.
For single-threaded, single-process compilers this can be
several times faster, provided that the interacting
with any input files is delayed until absolutely necessary.
For example, it is the case when the compiler process first
loads the standard library for several seconds and then
processes scratch files in several milliseconds.