-
Notifications
You must be signed in to change notification settings - Fork 1.8k
C#: Include runtime target in TTransitiveCaptureCall #1943
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
C#: Include runtime target in TTransitiveCaptureCall #1943
Conversation
TTransitiveCaptureCall represents a control flow node that may transitively call many different callables which capture a variable from the current scope. Captured variables are represented as synthetic parameters to the callable, at negative indices. However, each of the different targets may capture a different subset of variables from the enclosing scope, so we must include the target along side the CFN in order to prevent incorrect capture flow.
Add a commented out version of the test, and modify the expected files to contain the same results at new offsets.
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.
Thanks a lot for fixing this, Luke!
@@ -953,7 +953,7 @@ private module OutNodes { | |||
additionalCalls = false and | |||
call.(ImplicitDelegateDataFlowCall).isArgumentOf(csharpCall(_, cfn), _) | |||
or | |||
additionalCalls = true and call = TTransitiveCapturedCall(cfn) | |||
additionalCalls = true and call = TTransitiveCapturedCall(cfn, _) |
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 believe this should be TTransitiveCapturedCall(cfn, n.getEnclosingCallable())
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.
And would be nice with a test for that case as well, i.e., two nested lambdas that both write to a captured variable, but only one of them writes a tainted value.
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.
In theory, I think you're right. In practice, I was unable to find a test case where this actually made a difference. I think this is because OutNode.getCall()
is not used in practice in the dataflow libraries. Instead DataFlowDispatch::Cached::getAnOutNode(DataFlowCall call, ReturnKind kind) associates OutNode
s with DataFlowCall
s in such a way that the runtime target is respected. I could be wrong, though! 🙂
In any case, I've made the change as suggested, and added a test case for captured out variables (whose results did not change after this line was modified).
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.
You are right; in fact it is because the return kind, ImplicitCapturedReturnKind
, explicitly carries the captured variable that the two updates do not get mixed up.
In theory this bug could associated CaptureOutNodes with the wrong transitively called callable. However, in practice I could not create a test case that revealed incorrect behaviour. I've included one such test case in the commit. I believe that the cause of this is that OutNode::getACall() is not actually used in the data flow libraries. Instead, DataFlowDispatch::Cached::getAnOutNode is the predicate which is used to associated OutNode's with DataFlowCall's in practice, and that is always used in a context that correctly binds the runtime target of the call.
Is this something that should be mentioned in the release notes for 1.22.1? |
@felicitymay yes, sorry - I remembered only after Tom merged. I will open a separate PR to add a release note for this. |
Actually, @lcartey - at this point, you can simply add the change to the draft release notes page in the wiki. |
Brief change note added to: https://wiki.semmle.com/display/SDmaster/Semmle+1.22+-+release+notes following advice from Tom. |
TTransitiveCaptureCall
represents a control flow node that may transitively call many different callables which capture a variable from the current scope. Captured variables are represented as synthetic parameters to the callable, at negative indices. However, each of the different targets may capture a different subset of variables from the enclosing scope, so we must include the target callable in theTTransitiveCaptureCall
in order to prevent incorrect capture flow between unrelated capture variables.For example, in the included test case:
nonSink0
is captured in the outer lambda as a parameter at index-2
. However, the parameter at index-2
in the nested lambda issink39
. Without the runtime target context, we get flow from the parametersink39
to the access ofnonSink0
inCheck(nonSink0)
.Tests are added in two commits; the first adds the new test cases, but commented out, and adjusts the expected result files to use the new locations. The second commit uncomments the new tests, and adds the new rows to the expected result files
@hvitved I've submitted this against
rc/1.22
, as this issue is affecting a customer.