This is an archive of the discontinued LLVM Phabricator instance.

[ubsan] Sanitize deleted pointers
Needs ReviewPublic

Authored by gingell on Oct 3 2016, 10:58 AM.

Details

Summary

This patch adds a "value-after-delete" sanitizer, which will
invalidate the value of a pointer passed in a delete expression.

For instance, when -fsanitize=value-after-delete is passed:

int *foo = new int;
delete foo;
// foo == 0xDEADBEEFDEADBEEF

This is intended to help catch some use-after-free problems by
ensuring access through a deleted pointer fails immediately on
an address should be obviously suspicious when inspected in the
debugger. The expectation is immediately invalidating dangling
pointers can help uncover latent bugs that might otherwise cause
more subtle problems further down the line.

Diff Detail

Event Timeline

gingell updated this revision to Diff 73302.Oct 3 2016, 10:58 AM
gingell retitled this revision from to [ubsan] Sanitize deleted pointers .
gingell updated this object.
gingell added reviewers: cfe-commits, kcc.
vsk added a subscriber: vsk.Oct 3 2016, 11:24 AM

It looks like programs which trip -fsanitize-value-after-delete will just crash without further reporting, which isn't in keeping with the way other ubsan checks are implemented.

IMO, address sanitizer is better-equipped to diagnose this issue.

kcc added a reviewer: pcc.Oct 3 2016, 2:37 PM
In D25199#559407, @vsk wrote:

It looks like programs which trip -fsanitize-value-after-delete will just crash without further reporting, which isn't in keeping with the way other ubsan checks are implemented.

IMO, address sanitizer is better-equipped to diagnose this issue.

of course, but asan's overhead is much greater.
This tool might be fast enough to be always on in production.

The code looks reasonable to me, but as I am not too familiar with clang code, so asking pcc@ to have a look.

Also, please

  • update the ubsan documentation
  • add a runnable test to compiler-rt/test/ubsan
kcc added a comment.Oct 3 2016, 2:40 PM

will just crash without further reporting

I agree, and we can address that by having special logic in ubsan's segv handler.
This does not have to be in this patch.

Also, I am not sure about the actual constant.
DEADBEEF is commonly recognized poison valued, but on a 32-bit system it may actually be a valid pointer.

vsk added a comment.Oct 3 2016, 2:52 PM
In D25199#559797, @kcc wrote:

will just crash without further reporting

I agree, and we can address that by having special logic in ubsan's segv handler.
This does not have to be in this patch.

@kcc Is it safe to add a handler for segv and continue program execution as normal? I'm asking because I haven't tried that before, and am guessing you have experience with this from working on asan.

If there is a safe and portable way to call a ubsan diagnostic handler after hitting this error, then I agree that it would be very valuable.

One more thing to consider: how will we support -fsanitize-trap=value-after-delete?

lib/CodeGen/CGExprScalar.cpp
413

This is typically done by placing a call to e.g CGF.EmitValueAfterDeleteCheck, and then having an early return in EmitValueAfterDeleteCheck if the sanitizer isn't enabled.

414

Variables are usually capitalized.

418

This is missing a negative test.

test/CodeGenCXX/sanitize-value-after-delete.cpp
2

Why are the '-O3' and '-disable-llvm-optzns' flags needed here?

pcc added a reviewer: rsmith.Oct 3 2016, 2:59 PM

It seems to me that this sanitizer would break the semantics of otherwise well-defined programs. For example:

int *x = nullptr;
delete x;
if (x != nullptr) {
  // normally unreachable
}

It may be that a null comparison would be enough to avoid the semantics break, but I am not certain of this. Richard, what do you think?

vsk added a comment.Oct 3 2016, 3:06 PM
In D25199#559849, @pcc wrote:

It seems to me that this sanitizer would break the semantics of otherwise well-defined programs. For example:

int *x = nullptr;
delete x;
if (x != nullptr) {
  // normally unreachable
}

It may be that a null comparison would be enough to avoid the semantics break, but I am not certain of this.

Maybe we could call this -fpoison-dangling-ptrs and force users to be more explicit about opting into this behavior change. That would remove some of the constraints usually placed on new sanitizer checks (e.g support for executing after the error triggers, support for custom trap functions).

kcc added a comment.Oct 3 2016, 4:52 PM

Maybe we could call this -fpoison-dangling-ptrs and force users to be more explicit about opting into this behavior change. That would remove some of the constraints usually placed on new sanitizer checks (e.g support for executing after the error triggers, support for custom trap functions).

What's wrong with -fsanitize=SOMETHING? (the exact value of SOMETHING is debatable)

@kcc Is it safe to add a handler for segv and continue program execution as normal? I'm asking because I haven't tried that before, and am guessing you have experience with this from working on asan.

Not sure I understand your question.
We can add an optional SEGV handler to ubsan that will see the address on which we failed, and say something like "faulty address is close to 0xDEADBEEF, use of dangling pointer suspected. <STACK_TRACE>"
Then exit.

One more thing to consider: how will we support -fsanitize-trap=value-after-delete?

This will essentially only have the trap mode, it will not support -fsanitize-recover

include/clang/Basic/Sanitizers.def
105

Agree with Peter's concern -- it may break rare valid cases, so it should not be in this group.
We already have unsigned-integer-overflow that will also break valid code, so it's fine to have a second case like that.

vsk added a comment.Oct 3 2016, 5:14 PM
In D25199#560023, @kcc wrote:

Maybe we could call this -fpoison-dangling-ptrs and force users to be more explicit about opting into this behavior change. That would remove some of the constraints usually placed on new sanitizer checks (e.g support for executing after the error triggers, support for custom trap functions).

What's wrong with -fsanitize=SOMETHING? (the exact value of SOMETHING is debatable)

My concern is that using -fsanitize=something could confuse users who expect support for -fsanitize-trap-function=custom_func or -fsanitize-recover=something. But it's a minor concern. I am fine with -fsanitize=something if we leave it out of the default -fsanitize=undefined checks.

@kcc Is it safe to add a handler for segv and continue program execution as normal? I'm asking because I haven't tried that before, and am guessing you have experience with this from working on asan.

Not sure I understand your question.
We can add an optional SEGV handler to ubsan that will see the address on which we failed, and say something like "faulty address is close to 0xDEADBEEF, use of dangling pointer suspected. <STACK_TRACE>"
Then exit.

My question was about whether it's possible to resume normal program execution after printing the stack trace from the segv handler. I had assumed this is not possible, and (mistakenly) thought that you were suggesting this approach.

One more thing to consider: how will we support -fsanitize-trap=value-after-delete?

This will essentially only have the trap mode, it will not support -fsanitize-recover

OK.

filcab added a subscriber: filcab.Oct 4 2016, 8:25 AM
In D25199#560061, @vsk wrote:

My question was about whether it's possible to resume normal program execution after printing the stack trace from the segv handler. I had assumed this is not possible, and (mistakenly) thought that you were suggesting this approach.

I guess we can eventually add a warning if you have this check + trap-function. If there's really a need for it.

docs/UndefinedBehaviorSanitizer.rst
122

Why just delete and not free()?

lib/CodeGen/CGExprScalar.cpp
416

Missing a test for this condition.

test/CodeGenCXX/sanitize-value-after-delete.cpp
2

Please keep the test simple. You don't even need C++11 (in addition to the flags vsk mentioned).

22

Why?