-
Notifications
You must be signed in to change notification settings - Fork 126
Fix and improve taint-tracking through function arguments #129
Fix and improve taint-tracking through function arguments #129
Conversation
…from results to arguments.
The query under test isn't a `@problem` query, so we should refer to "alerts".
ghost
left a comment
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.
LGTM sans a minor nit.
| pun = result and | ||
| init = pun.(DataFlow::SsaNode).getInit() | ||
| | | ||
| index = -1 and |
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 am not sure I understand this well yet but why is the index start from -1?
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.
If there is more than one result, we number them starting from zero. If there is only a single result, we number it as -1. It's an implementation detail that isn't exposed in the API, so nothing to worry about.
| SmtpData() { | ||
| // func (c *Client) Data() (io.WriteCloser, error) | ||
| exists(Method data, DataFlow::CallNode write, DataFlow::Node writer, int i | | ||
| exists(Method 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.
Since, we are changing this, we should also change the ResponseBody function in HTTP.qll.
codeql-go/ql/src/semmle/go/frameworks/HTTP.qll
Lines 144 to 162 in ca0d9cc
| private class ResponseBody extends HTTP::ResponseBody::Range, DataFlow::ArgumentNode { | |
| int arg; | |
| ResponseBody() { | |
| exists(DataFlow::CallNode call | | |
| call.getTarget().(Method).implements("net/http", "ResponseWriter", "Write") and | |
| arg = 0 | |
| or | |
| ( | |
| call.getTarget().hasQualifiedName("fmt", "Fprintf") | |
| or | |
| call.getTarget().hasQualifiedName("io", "WriteString") | |
| ) and | |
| call.getArgument(0).getType().hasQualifiedName("net/http", "ResponseWriter") and | |
| arg >= 1 | |
| | | |
| this = call.getArgument(arg) | |
| ) | |
| } |
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 principle I agree, but in practice that is a little more difficult to change, since that class wants to refer both to the original source of the data as well as to the writer it is written to.
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.
Yes, I missed that. This can now be resolved.
sauyon
left a comment
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.
Two minor comments.
I took a look at a few of the results, and I think we should probably exclude Fprint calls that aren't printing to stderr or a file. At least some of them seem like true positives though.
| or | ||
| preupd instanceof ArgumentNode and | ||
| mutableType(preupd.getType()) | ||
| mutableType(preupd.getType().getUnderlyingType()) |
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 guess this isn't so important since it's only used here, but maybe mutableType should do this inside the predicate?
| Sscanner() { this.hasQualifiedName("fmt", ["Sscan", "Sscanf", "Sscanln"]) } | ||
|
|
||
| override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { | ||
| input.isParameter(0) and output.isParameter(any(int i | i > 0)) |
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 don't think the format string for Sscanf (argument 1) be considered as an output?
|
Thank you for the review, @sauyon. I have addressed your suggestions and made an issue about improving the |
This PR got started with me trying to understand why tainting function arguments didn't seem to work. The fix for that turned out to be simple; it's in the first commit.
When trying to model taint propagation through writers, I quickly noticed that I often wanted a sort of "backwards" taint propagation: many writer APIs involve wrapping some basic writer
bwinto a higher-level writerw, and it's quite natural to expect that taint propagates fromwback tobw.To model this nicely, I introduced a new kind of
FunctionInputthat corresponds to the result of a function. It looks a little weird at first, but is nicely symmetric with the other cases we already support. With a little more API modeling we can then cover a false negative discussed in #108.Finally, while working on our standard-library modeling I noticed that our models of
Printfand friends as sinks for clear-text logging were broken due to a typo, which I fixed.An evaluation shows no performance regressions, but quite a few new results from the fix to clear-text logging.
I looked at a few and they didn't look a priori implausible. Nevertheless we should consider carefully whether we want all those new results. If not, it's easy enough to remove the newly added sinks (and he change note).