[clang] [Clang] Fix potential null pointer dereferences in Sema::AddInitializerToDecl (PR #94368)
Tom Honermann via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 7 11:45:55 PDT 2024
tahonermann wrote:
I don't think the currently proposed change is right for a few reasons.
The proposed change silently marks the declaration as invalid and performs an early return. I don't think this should be a silent behavior. In most, if not all, of the existing early return cases, a diagnostic is issued (perhaps via another called function) and/or a recovery expression is created and used as the initializer.
I think this prior statement of mine is incorrect:
> On the one hand, the assignment to `Init` looks to me like it must produce a non-null result due to the prior check to `Result.isInvalid()`.
It is possible for an `ExprResult` value to be valid and still hold a null pointer value; `InitializationSequence::Perform()` explicitly returns a (valid) null pointer in at least one case:
```
clang/lib/Sema/SemaInit.cpp:
8612 ExprResult InitializationSequence::Perform(Sema &S,
8613 const InitializedEntity &Entity,
8614 const InitializationKind &Kind,
8615 MultiExprArg Args,
8616 QualType *ResultType) {
....
8699 // No steps means no initialization.
8700 if (Steps.empty())
8701 return ExprResult((Expr *)nullptr);
....
9539 }
```
This indicates that, absent a more complicated analysis that proves that `Sema::AddInitializerToDecl()` will never call `InitializationSequence::Perform()` in a context that will lead to a null result, the static analysis tool is right to report a possible null dereference.
I suggest adding an assert here:
```
clang/lib/Sema/SemaDecl.cpp:
13660 if (!VDecl->isInvalidDecl()) {
.....
13690 ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Args, &DclT);
13691 if (Result.isInvalid()) {
.....
13707 return;
13708 }
13709
13710 Init = Result.getAs<Expr>();
.....
13726 }
+
+ assert(Init && "Should have a valid initializer at this point);
+
13728 // Check for self-references within variable initializers.
13729 // Variables declared within a function/method body (except for references)
13730 // are handled by a dataflow analysis.
13731 // This is undefined behavior in C++, but valid in C.
13732 if (getLangOpts().CPlusPlus)
13733 if (!VDecl->hasLocalStorage() || VDecl->getType()->isRecordType() ||
13734 VDecl->getType()->isReferenceType())
13735 CheckSelfReference(*this, RealDecl, Init, DirectInit);
```
Relatedly, I think the two checks for `Init` being non-null are probably wrong in these cases:
```
13506 // WebAssembly tables can't be used to initialise a variable.
13507 if (Init && !Init->getType().isNull() &&
13508 Init->getType()->isWebAssemblyTableType()) {
13509 Diag(Init->getExprLoc(), diag::err_wasm_table_art) << 0;
13510 VDecl->setInvalidDecl();
13511 return;
13512 }
.....
13715 if (Init && !Init->getType().isNull() &&
13716 !Init->getType()->isDependentType() && !VDeclType->isDependentType() &&
13717 Context.getAsIncompleteArrayType(VDeclType) &&
13718 Context.getAsIncompleteArrayType(Init->getType())) {
13719 // Bail out if it is not possible to deduce array size from the
13720 // initializer.
13721 Diag(VDecl->getLocation(), diag::err_typecheck_decl_incomplete_type)
13722 << VDeclType;
13723 VDecl->setInvalidDecl();
13724 return;
13725 }
```
I think the null check at line 13507 can just be removed; previous uses of `Init` appear to expect a non-null value. This looks like a case of a reflexive defensive check (added in https://reviews.llvm.org/D139010).
The null check at line 13715 also looks like it was added as a reflexive defensive check (added in https://reviews.llvm.org/D158615 for https://github.com/llvm/llvm-project/issues/37257)
Adding the assert and removing the defensive null checks would eliminate the evidence the static analysis tool is using to justify its report of a potential null dereference.
If Clang tests pass with these changes (using an asserts enabled build of course!), then I think we would be ok to proceed. If we then see failed assertions or null pointer dereferences at some point, we'll be able to identify test cases to add.
https://github.com/llvm/llvm-project/pull/94368
More information about the cfe-commits
mailing list