This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Fix crash for array-delete of UnknownVal values.
ClosedPublic

Authored by tomasz-kaminski-sonarsource on Oct 25 2022, 2:26 AM.

Details

Summary

We now skip the destruction of array elements for delete[] p,
if the value of p is UnknownVal and does not have corresponding region.
This eliminate the crash in getDynamicElementCount on that
region and matches the behavior for deleting the array of
non-constant range.

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald Transcript
tomasz-kaminski-sonarsource requested review of this revision.Oct 25 2022, 2:26 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 25 2022, 2:26 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

This eliminate the crash in getDynamicElementCount on that region

I think that if the crash comes from getDynamicElementCount, we should address it there too, so
when the function is called elsewhere under the same circumstances it won't crash again.

What do you think?

This eliminate the crash in getDynamicElementCount on that region

I think that if the crash comes from getDynamicElementCount, we should address it there too, so
when the function is called elsewhere under the same circumstances it won't crash again.

What do you think?

Good point .
I was also wondering about it and went for the current approach or preserving the precondition that the passed region cannot be null, because:

  • For other uses of prepareStateForArrayDestruction in case of automatic variables or members, the destroyed should be always known, so this precondition makes sense. In this context delete[] p of unknown pointer is special case, so localized handling made sense.
  • This case of ArgR being null was already handled in the code, so extending it seemed to be more consistent.

Does it make sense to you?

This eliminate the crash in getDynamicElementCount on that region

I think that if the crash comes from getDynamicElementCount, we should address it there too, so
when the function is called elsewhere under the same circumstances it won't crash again.

What do you think?

For me, it makes sense that getDynamicElementCount() is not intended to be called on a null MemRegion. So, from my point of view, the bug is at some callsite.

For me, it makes sense that getDynamicElementCount() is not intended to be called on a null MemRegion.

It would still be helpful to have an assertion on that inside getDynamicElementCount(), so the caller will know it right away.

What I want to say is that it seems there are multiple error prone snippets involved in this crash and I think we should address all
of them, because some might cause another crash somewhere else.

isuckatcs added inline comments.Nov 1 2022, 12:36 PM
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
1244

I meant to assert this inside getDynamicElementCount() as that's the function that dereferences the pointer.

DefinedOrUnknownSVal getDynamicElementCount(..., const MemRegion *MR, ...) {
  MR = MR->StripCasts();                                                            <-- crash here if MR is a nullptr

  DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB);
  SVal ElementSize = getElementExtent(ElementTy, SVB);

  SVal ElementCount =
      SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType());     <-- this could be the origin of 
                                                                                        a crash too

  return ElementCount.castAs<DefinedOrUnknownSVal>();
}

Also SVB.evalBinOp() can technically return an UndefinedVal and if it does that, then ElementCount.castAs<DefinedOrUnknownSVal>() will also cause a crash as it happened in D130974.

Also since this function can return an UnknownSVal, what do you think about returning that if MR is a nullptr instead of stopping execution? Would that behaviour even make sense?

tomasz-kaminski-sonarsource marked an inline comment as done.

Added assert

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
1244

I meant to assert this inside getDynamicElementCount() as that's the function that dereferences the pointer.

DefinedOrUnknownSVal getDynamicElementCount(..., const MemRegion *MR, ...) {
  MR = MR->StripCasts();                                                            <-- crash here if MR is a nullptr

  DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB);
  SVal ElementSize = getElementExtent(ElementTy, SVB);

  SVal ElementCount =
      SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType());     <-- this could be the origin of 
                                                                                        a crash too

  return ElementCount.castAs<DefinedOrUnknownSVal>();
}

Also SVB.evalBinOp() can technically return an UndefinedVal and if it does that, then ElementCount.castAs<DefinedOrUnknownSVal>() will also cause a crash as it happened in D130974.

Also since this function can return an UnknownSVal, what do you think about returning that if MR is a nullptr instead of stopping execution? Would that behaviour even make sense?

1244

I meant to assert this inside getDynamicElementCount() as that's the function that dereferences the pointer.

I see. I will add the assert.

Also since this function can return an UnknownSVal, what do you think about returning that if MR is a nullptr instead of stopping execution? Would that behaviour even make sense?

I still believe that passing nullptr as MR to this function is bug on the caller side, and not expected behavior. This is why I would prefer to have to assert here, instead of returning some symbol, as this will hide such bugs.

isuckatcs accepted this revision.Nov 7 2022, 2:51 AM

I have nothing else to add, this patch looks good to me. If the others don't have anything to add either, feel free to commit.

This revision is now accepted and ready to land.Nov 7 2022, 2:51 AM