Skip to content

JS: Barrier guard improvements #2251

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

Merged
merged 4 commits into from
Nov 7, 2019

Conversation

asger-semmle
Copy link
Contributor

A few improvements to barrier/sanitizer guards, motivated by recent query work:

  • Support arbitrarily many && and || operators in sanitizer guard functions
  • Generalize sanitizer guard functions to work with any data-flow configuration and flow labels.
  • Support reflective calls in barrier guards

Commit-by-commit review recommended.

Evaluation was uneventful. Columns 1 and 2 correspond to this PR, and column 3 is another commit that I dropped again.

@asger-semmle asger-semmle added the JS label Nov 4, 2019
@asger-semmle asger-semmle requested a review from a team as a code owner November 4, 2019 15:50
@erik-krogh
Copy link
Contributor

The tests are failing.

ql/javascript/ql/test/library-tests/TaintBarriers/SanitizingGuard.ql: Test failure (Expected output does not match)
ql/javascript/ql/test/library-tests/TaintBarriers/TaintedSink.ql: Test failure (Expected output does not match)
ql/javascript/ql/test/library-tests/TaintBarriers/isBarrier.ql: Test failure (Expected output does not match)

@asger-semmle
Copy link
Contributor Author

Indeed. Seems to be benign changes though.

pragma[noinline]
private predicate internalBlocksAccessPath(boolean outcome, AccessPath ap, string label) {
internalBlocksExpr(outcome, ap.getAnInstance(), label)
this = DataFlow::reflectiveCallNode(result)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've previously had the same issue as the one you are solving here.

Could there be an idea in changing DataFlow::Node::asExpr() to include reflective calls, or lift this method to DataFlow::Node?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case getExpr might not be the most descriptive name.

asExprInclReflectiveCalls? (But that name feels too long, feel free to ignore).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could there be an idea in changing DataFlow::Node::asExpr()

I think that leads to a recursion mess, and also a mess of contepts. I would prefer the second option:

lift this method to DataFlow::Node

But a good name escapes me. asUnderlyingExpr is my best suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main gripe with using asExpr() is that two data flow nodes can suddenly map to the same expression, which brings along its own set of new pitfalls. But maybe we should do it anyway? It's essentially the same issue as parameter nodes not having AST nodes.

The general solution is to take the thing you know is an expression (getTest()), map that to a data-flow node, and follow up with getImmediatePredecessor, getAPredecessor, or getALocalSource. The third version I mentioned in the PR description used getImmediatePredecessor but I backed it out again, because it required more work for that generalization to actually work beyond reflective calls.

I think I've previously had the same issue as the one you are solving here.

@erik-krogh Can you find out what it was? It might help us figure out which solution works best.

Copy link
Contributor

@erik-krogh erik-krogh Nov 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@erik-krogh Can you find out what it was? It might help us figure out which solution works best.

From looking around I can't immediately find an instance of me solving the issue, but I think that is because I ended up not solving it.
The issue definitely exists in UseOfReturnlessFunction, which has no explicit handling of reflective calls, and DataFlow::CallNode::asExpr() is therefore none().

As a consequence, UseOfReturnlessFunction has an FP on the last line in this example:

function onlySideEffects() {
  console.log("Boo!")
}
var dead1 = onlySideEffects(); // Correctly not flagged
var dead2 = onlySideEffects.call(null); // FP! flagged by UseOfReturnlessFunction

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about DataFlow::Node::getEnclosingExpr() or getEnclosingAstNode()? This would map reflective calls to the CallExpr, parameter nodes to the Parameter, and ValueNode to the AST node.

(To elaborate on my previous point, extending asExpr() and flow() can lead to unfortunate cases where code like expr.flow().(CallNode).getArgument(0) returns the first two arguments.)

Anyways, I think it's a bit out of scope for this PR, but I'm happy to continue the discussion here. Ping @max-schaefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently doing a dist-compare on an implementation of DataFlow::Node::getEnclosingExpr().

Expr getEnclosingExpr() {
  result = asExpr() or
  this = DataFlow::reflectiveCallNode(result) or
  result = this.(ParameterNode).getParameter()
}

The evaluation is on UseOfReturnlessFunction, where i changed some asExpr() to getEnclosingExpr(), which definitely removes some false positives.
If the results are good I'll land it in a PR.

@asger-semmle
Copy link
Contributor Author

Any comments apart from the asExpr() tangent?

Copy link
Contributor

@esbena esbena left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not from me. Lets assume #2270 can replace getExpr without a cost when it lands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants