Skip to content

C++: Workaround for problem with memcpy flow #2736

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
Feb 3, 2020

Conversation

jbj
Copy link
Contributor

@jbj jbj commented Jan 31, 2020

The type of the source argument to memcpy is void *, and somehow that meant that the copied object itself got type void. Since that has size 0, the SSA construction did not model it as reading from the last write.

This is probably not the right fix, but maybe it's good enough for now. The right fix would ensure that the type reported by hasOperandMemoryAccess is UnknownType.

When DefaultTaintTracking.qll is enabled, this commit has the effect of restoring a lost results:

--- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
@@ -1 +1,2 @@
 | overflowdestination.cpp:30:2:30:8 | call to strncpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
+| overflowdestination.cpp:46:2:46:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |

The type of the source argument to `memcpy` is `void *`, and somehow
that meant that the copied object itself got type `void`. Since that has
size 0, the SSA construction did not model it as reading from the last
write.

This is probably not the right fix, but maybe it's good enough for now.
The right fix would ensure that the type reported by
`hasOperandMemoryAccess` is `UnknownType`.

When `DefaultTaintTracking.qll` is enabled, this commit has the effect
of restoring a lost results:

    --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    @@ -1 +1,2 @@
     | overflowdestination.cpp:30:2:30:8 | call to strncpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
    +| overflowdestination.cpp:46:2:46:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
@jbj jbj added the C++ label Jan 31, 2020
@jbj jbj requested review from rdmarsh2 and dbartol January 31, 2020 15:19
@jbj jbj requested a review from a team as a code owner January 31, 2020 15:19
jbj added a commit to jbj/ql that referenced this pull request Jan 31, 2020
As a temporary workaround in the `DefaultTaintTracking` library, we
funnel flow across calls by conflating pointer and object both at the
caller and the callee.

The three cases in `adjustedSink` were deleted because they are now
covered by the one case for `ReadSideEffectInstruction` in
`instructionTaintStep`.

When enabling `DefaultTaintTracking`, this commit on top of github#2736 has
the effect effect of recovering two lost results:

    --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    @@ -1,2 +1,4 @@
     | overflowdestination.cpp:30:2:30:8 | call to strncpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
     | overflowdestination.cpp:46:2:46:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
    +| overflowdestination.cpp:53:2:53:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
    +| overflowdestination.cpp:64:2:64:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |

In the internal repo, we recover one lost result. Additionally, there
are two queries that gain an extra source for an existing sink. I'll
classify that as noise. The new results look like this:

    foo(argv); // this `argv` is a new source for the sink in `bar`
    bar(argv); // this `argv` is the existing source for the sink in `bar`
@rdmarsh2
Copy link
Contributor

I've added an IR test to demonstrate the old issue. I wouldn't be surprised if there's other weird implications of the void operand type, but I can't think of any specific ones.

@jbj
Copy link
Contributor Author

jbj commented Feb 3, 2020

@rdmarsh2 Your three commits LGTM. Does my original commit LGTY?

@rdmarsh2 rdmarsh2 merged commit f51841a into github:master Feb 3, 2020
MathiasVP pushed a commit to MathiasVP/ql that referenced this pull request Feb 4, 2020
As a temporary workaround in the `DefaultTaintTracking` library, we
funnel flow across calls by conflating pointer and object both at the
caller and the callee.

The three cases in `adjustedSink` were deleted because they are now
covered by the one case for `ReadSideEffectInstruction` in
`instructionTaintStep`.

When enabling `DefaultTaintTracking`, this commit on top of github#2736 has
the effect effect of recovering two lost results:

    --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    @@ -1,2 +1,4 @@
     | overflowdestination.cpp:30:2:30:8 | call to strncpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
     | overflowdestination.cpp:46:2:46:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
    +| overflowdestination.cpp:53:2:53:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
    +| overflowdestination.cpp:64:2:64:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |

In the internal repo, we recover one lost result. Additionally, there
are two queries that gain an extra source for an existing sink. I'll
classify that as noise. The new results look like this:

    foo(argv); // this `argv` is a new source for the sink in `bar`
    bar(argv); // this `argv` is the existing source for the sink in `bar`
MathiasVP pushed a commit to MathiasVP/ql that referenced this pull request Feb 4, 2020
As a temporary workaround in the `DefaultTaintTracking` library, we
funnel flow across calls by conflating pointer and object both at the
caller and the callee.

The three cases in `adjustedSink` were deleted because they are now
covered by the one case for `ReadSideEffectInstruction` in
`instructionTaintStep`.

When enabling `DefaultTaintTracking`, this commit on top of github#2736 has
the effect effect of recovering two lost results:

    --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected
    @@ -1,2 +1,4 @@
     | overflowdestination.cpp:30:2:30:8 | call to strncpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
     | overflowdestination.cpp:46:2:46:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
    +| overflowdestination.cpp:53:2:53:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
    +| overflowdestination.cpp:64:2:64:7 | call to memcpy | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |

In the internal repo, we recover one lost result. Additionally, there
are two queries that gain an extra source for an existing sink. I'll
classify that as noise. The new results look like this:

    foo(argv); // this `argv` is a new source for the sink in `bar`
    bar(argv); // this `argv` is the existing source for the sink in `bar`
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.

2 participants