Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

// RUN: %clang_analyze_cc1 -analyzer-eagerly-assume -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s
// RUN: %clang_analyze_cc1 -analyzer-eagerly-assume -analyzer-checker=core -verify -DSUPPRESSED=1 %s
// RUN: %clang_analyze_cc1 -analyzer-eagerly-assume -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s

int opaquePropertyCheck(void *object);
int coin();

int *getNull() {
  return 0;
}

int* getPtr();

int *dynCastToInt(void *ptr) {
  if (opaquePropertyCheck(ptr))
    return (int *)ptr;
  return 0;
}

int *dynCastOrNull(void *ptr) {
  if (!ptr)
    return 0;
  if (opaquePropertyCheck(ptr))
    return (int *)ptr;
  return 0;
}


void testDynCast(void *p) {
  int *casted = dynCastToInt(p);
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

void testDynCastOrNull(void *p) {
  int *casted = dynCastOrNull(p);
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}


void testBranch(void *p) {
  int *casted;

  // Although the report will be suppressed on one branch, it should still be
  // valid on the other.
  if (coin()) {
    casted = dynCastToInt(p);
  } else {
    if (p)
      return;
    casted = (int *)p;
  }

  *casted = 1; // expected-warning {{Dereference of null pointer}}
}

void testBranchReversed(void *p) {
  int *casted;

  // Although the report will be suppressed on one branch, it should still be
  // valid on the other.
  if (coin()) {
    if (p)
      return;
    casted = (int *)p;
  } else {
    casted = dynCastToInt(p);
  }

  *casted = 1; // expected-warning {{Dereference of null pointer}}
}

void testMultipleStore(void *p) {
  int *casted = 0;
  casted = dynCastToInt(p);
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

// Test that div by zero does not get suppressed. This is a policy choice.
int retZero() {
  return 0;
}
int triggerDivZero () {
  int y = retZero();
  return 5/y; // expected-warning {{Division by zero}}
}

// Treat a function-like macro similarly to an inlined function, so suppress
// warnings along paths resulting from inlined checks.
#define MACRO_WITH_CHECK(a) ( ((a) != 0) ? *a : 17)
void testInlineCheckInMacro(int *p) {
  int i = MACRO_WITH_CHECK(p);
  (void)i;

  *p = 1; // no-warning
}

#define MACRO_WITH_NESTED_CHECK(a) ( { int j = MACRO_WITH_CHECK(a); j; } )
void testInlineCheckInNestedMacro(int *p) {
  int i = MACRO_WITH_NESTED_CHECK(p);
  (void)i;

  *p = 1; // no-warning
}

#define NON_FUNCTION_MACRO_WITH_CHECK ( ((p) != 0) ? *p : 17)
void testNonFunctionMacro(int *p) {
  int i = NON_FUNCTION_MACRO_WITH_CHECK ;
  (void)i;

  *p = 1; // no-warning
}


// This macro will dereference its argument if the argument is NULL.
#define MACRO_WITH_ERROR(a) ( ((a) != 0) ? 0 : *a)
void testErrorInMacro(int *p) {
  int i = MACRO_WITH_ERROR(p); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
  (void)i;
}

// Here the check (the "if") is not in a macro, so we should still warn.
#define MACRO_IN_GUARD(a) (!(a))
void testMacroUsedAsGuard(int *p) {
  if (MACRO_IN_GUARD(p))
    *p = 1; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
}

// When a nil case split is introduced in a macro and the macro is in a guard,
// we still shouldn't warn.
int isNull(int *p);
int isEqual(int *p, int *q);
#define ISNULL(ptr)    ((ptr) == 0 || isNull(ptr))
#define ISEQUAL(a, b)    ((int *)(a) == (int *)(b) || (ISNULL(a) && ISNULL(b)) || isEqual(a,b))
#define ISNOTEQUAL(a, b)   (!ISEQUAL(a, b))
void testNestedDisjunctiveMacro(int *p, int *q) {
  if (ISNOTEQUAL(p,q)) {
    *p = 1; // no-warning
    *q = 1; // no-warning
  }

  *p = 1; // no-warning
  *q = 1; // no-warning
}

void testNestedDisjunctiveMacro2(int *p, int *q) {
  if (ISEQUAL(p,q)) {
    return;
  }

  *p = 1; // no-warning
  *q = 1; // no-warning
}



// Here the check is entirely in non-macro code even though the code itself
// is a macro argument.
#define MACRO_DO_IT(a) (a)
void testErrorInArgument(int *p) {
  int i = MACRO_DO_IT((p ? 0 : *p)); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}c
  (void)i;
}

// No warning should be emitted if dereference is performed from a different
// macro.
#define MACRO_CHECK(a) if (a) {}
#define MACRO_DEREF(a) (*a)
int testDifferentMacro(int *p) {
  MACRO_CHECK(p);
  return MACRO_DEREF(p); // no-warning
}

// --------------------------
// "Suppression suppression"
// --------------------------

void testDynCastOrNullOfNull() {
  // Don't suppress when one of the arguments is NULL.
  int *casted = dynCastOrNull(0);
  *casted = 1;
#if !SUPPRESSED || NULL_ARGS
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

void testDynCastOfNull() {
  // Don't suppress when one of the arguments is NULL.
  int *casted = dynCastToInt(0);
  *casted = 1;
#if !SUPPRESSED || NULL_ARGS
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

int *lookUpInt(int unused) {
  if (coin())
    return 0;
  static int x;
  return &x;
}

void testZeroIsNotNull() {
  // /Do/ suppress when the argument is 0 (an integer).
  int *casted = lookUpInt(0);
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

void testTrackNull() {
  // /Do/ suppress if the null argument came from another call returning null.
  int *casted = dynCastOrNull(getNull());
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

void testTrackNullVariable() {
  // /Do/ suppress if the null argument came from another call returning null.
  int *ptr;
  ptr = getNull();
  int *casted = dynCastOrNull(ptr);
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

void inlinedIsDifferent(int inlined) {
  int i;

  // We were erroneously picking up the inner stack frame's initialization,
  // even though the error occurs in the outer stack frame!
  int *p = inlined ? &i : getNull();

  if (!inlined)
    inlinedIsDifferent(1);

  *p = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

void testInlinedIsDifferent() {
  // <rdar://problem/13787723>
  inlinedIsDifferent(0);
}


// ---------------------------------------
// FALSE NEGATIVES (over-suppression)
// ---------------------------------------

void testNoArguments() {
  // In this case the function has no branches, and MUST return null.
  int *casted = getNull();
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

int *getNullIfNonNull(void *input) {
  if (input)
    return 0;
  static int x;
  return &x;
}

void testKnownPath(void *input) {
  if (!input)
    return;

  // In this case we have a known value for the argument, and thus the path
  // through the function doesn't ever split.
  int *casted = getNullIfNonNull(input);
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

int *alwaysReturnNull(void *input) {
  if (opaquePropertyCheck(input))
    return 0;
  return 0;
}

void testAlwaysReturnNull(void *input) {
  // In this case all paths out of the function return 0, but they are all
  // dominated by a branch whose condition we don't know!
  int *casted = alwaysReturnNull(input);
  *casted = 1;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

int derefArg(int *p) {
	return *p;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}
void ternaryArg(char cond) {
	static int x;
	derefArg(cond ? &x : getNull());
}

int derefArgCast(char *p) {
	return *p;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}
void ternaryArgCast(char cond) {
	static int x;
	derefArgCast((char*)((unsigned)cond ? &x : getNull()));
}

int derefAssignment(int *p) {
	return *p;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

void ternaryAssignment(char cond) {
  static int x;
  int *p = cond ? getNull() : getPtr();
  derefAssignment(p);
}

int *retNull(char cond) {
  static int x;
  return cond ? &x : getNull();
}
int ternaryRetNull(char cond) {
  int *p = retNull(cond);
  return *p;
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}

// Test suppression of nested conditional operators.
int testConditionalOperatorSuppress(int x) {
  return *(x ? getNull() : getPtr());
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}
int testNestedConditionalOperatorSuppress(int x) {
  return *(x ? (x ? getNull() : getPtr()) : getPtr());
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}
int testConditionalOperator(int x) {
  return *(x ? 0 : getPtr()); // expected-warning {{Dereference of null pointer}}
}
int testNestedConditionalOperator(int x) {
  return *(x ? (x ? 0 : getPtr()) : getPtr()); // expected-warning {{Dereference of null pointer}}
}

int testConditionalOperatorSuppressFloatCond(float x) {
  return *(x ? getNull() : getPtr());
#ifndef SUPPRESSED
  // expected-warning@-2 {{Dereference of null pointer}}
#endif
}