[PATCH] D14014: Checker of proper vfork usage
Yury Gribov via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 23 13:56:36 PDT 2015
ygribov added a comment.
> I didn't know vfork() is in regular use.
Neither did I, until I had to debug the damned code. Actually Android has some 5 or 6 uses of it.
> I'm also not convinced that flagging all stores in the vfork child process is the right thing to do,
> since the FP rate could be quite high.
I also have these concerns. Manpage is very strict about behavior of vforked processes (for a good reason though). Currently most uses of vfork are incorrect according to this checker.
I think there is some potential to relax the constraints:
- at the very least we can allow calls to functions marked as inline as those won't result in any memory writes
- we can even allow to call any functions which are guaranteed to not change global state
> For example, doing 'x = malloc(4); *x = 0;' in the child process seems fine to me.
I also thought about similar examples. Here are some not so obvious side-effects:
- you'll get a memory leak (as malloc will be shared by parent and child processes)
- in the following case behavior of parent is undefined because original value of variable will be corrupted:
x = some_very_important_lost;
if (vfork()==0) {
x = ...; // some_very_important_data is lost
_exit(1);
}
// parent code
if (some_very_important_data) {
// UB
}
- even if child does not explicitly write any parent's variables, compiler may decide to merge stack slots so write into a local variable in child process may corrupt a seemingly unrelated variable in parent
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:10
@@ +9,3 @@
+//
+// This file defines chroot checker, which checks improper use of chroot.
+//
----------------
vsk wrote:
> This comment needs an update.
Yikes, thanks for pointing this out.
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:38
@@ +37,3 @@
+// |
+// --return--> bug
+class VforkChecker : public Checker<check::PreCall, check::PostCall,
----------------
vsk wrote:
> Please clarify this comment -- it might be better to state a list of specific buggy conditions.
>
> E.g, are writes into heap-allocated allowed? Where are return statements allowed?
This primitive scheme simply mirrors the manpage which is very strict about this - write to _any_ data besides return value of vfork is disallowed, ditto for function calls (and so "return" only applies to current function).
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:44
@@ +43,3 @@
+
+ // This bug refers to dangerous code in vforked child.
+ mutable std::unique_ptr<BuiltinBug> BT_BadChildCode;
----------------
vsk wrote:
> This comment isn't needed -- a description to this effect can go in the top-of-file comment.
Right.
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:65
@@ +64,3 @@
+
+ // these constructs are prohibited in vforked child
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
----------------
vsk wrote:
> This comment doesn't add much, please remove it.
Ok.
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:75
@@ +74,3 @@
+REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkLhs, const void *)
+#define VFORK_LHS_NONE ((void *)(uintptr_t)1)
+
----------------
vsk wrote:
> Why not nullptr? If there's a good reason, please explain it with a comment.
Ok, this indeed deserves a comment. FTR: nullptr means parent process, VFORK_LHS_NONE means vforked process without explicit LHS and rest means LHS variable.
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:147
@@ +146,3 @@
+ return D;
+ } while (0);
+
----------------
vsk wrote:
> This is hard to read, please simplify this. E.g;
>
> if (PD = dyn-cast(P)) { /* handle cases where P is-a DeclStmt */ }
> else if (Assign = dyn-cast(P)) { /* handle cases where P is-a binop */ }
>
> Using loop constructs here shouldn't be necessary.
In this case each statement depends on the previous one so we'll have to resort to deeply nested conditionals. I thought that do-while-false is a common pattern for simplifying such constructs but I may be wrong.
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:167
@@ +166,3 @@
+ // otherwise no lhs
+ return (const VarDecl *)VFORK_LHS_NONE;
+}
----------------
vsk wrote:
> Typically nullptr is used here.
See my comment above, these two encode two different things.
================
Comment at: lib/StaticAnalyzer/Checkers/VforkChecker.cpp:186
@@ +185,3 @@
+ CheckerContext &C) const {
+ // we can't call vfork in child so don't bother
+ ProgramStateRef State = C.getState();
----------------
dcoughlin wrote:
> vsk wrote:
> > Actually, if this is the case, wouldn't it be worthwhile to flag calls to vfork() in child processes?
> This will get caught by `checkPreCall`, right? Because `vfork` is not in the white list.
They will be flagged in checkPreCall. But I should probably add a comment as you're the second reviewer who complained about this.
Repository:
rL LLVM
http://reviews.llvm.org/D14014
More information about the cfe-commits
mailing list