This static analysis will identify and reject callers where folks are passing nsTStringRepr::BeginReading() or ::Data() to printf-style functions. These functions are not guaranteed to return a null terminated string, so should not be used in these places. Most places which hit this analysis should not actually be security bugs, but we should fix them anyways, as people will use them as examples of what to do and write bad code based off of them. Differential Revision: https://phabricator.services.mozilla.com/D286156
116 lines
4.5 KiB
C++
116 lines
4.5 KiB
C++
#define MOZ_NON_TERMINATED_STRING __attribute__((annotate("moz_non_terminated_string")))
|
|
|
|
#include "mozilla/Casting.h"
|
|
|
|
#include <cstdio>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#define MOZ_FORMAT_PRINTF(stringIndex, firstToCheck) \
|
|
__attribute__((format(printf, stringIndex, firstToCheck)))
|
|
|
|
const char *getNotTerminated() MOZ_NON_TERMINATED_STRING;
|
|
const char *getTerminated();
|
|
|
|
void myPrintf(const char *fmt, ...) MOZ_FORMAT_PRINTF(1, 2);
|
|
|
|
struct S {
|
|
size_t size();
|
|
const char *data() MOZ_NON_TERMINATED_STRING;
|
|
const char *c_str();
|
|
};
|
|
|
|
void testPrintf() {
|
|
printf("%s", getNotTerminated()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to printf-like function 'printf'}}
|
|
printf("%s", getTerminated());
|
|
printf("hello %s world", getNotTerminated()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to printf-like function 'printf'}}
|
|
}
|
|
|
|
void testSnprintf(char *buf, int size) {
|
|
snprintf(buf, size, "%s", getNotTerminated()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to printf-like function 'snprintf'}}
|
|
snprintf(buf, size, "%s", getTerminated());
|
|
}
|
|
|
|
void testFprintf() {
|
|
fprintf(stderr, "%s", getNotTerminated()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to printf-like function 'fprintf'}}
|
|
fprintf(stderr, "%s", getTerminated());
|
|
}
|
|
|
|
void testCustomPrintf() {
|
|
myPrintf("%s", getNotTerminated()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to printf-like function 'myPrintf'}}
|
|
myPrintf("%s", getTerminated());
|
|
}
|
|
|
|
void testMethod() {
|
|
S s;
|
|
printf("%s", s.data()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'S::data' passed as an argument to printf-like function 'printf'}}
|
|
printf("%s", s.c_str());
|
|
}
|
|
|
|
struct PrintfCtor {
|
|
explicit PrintfCtor(const char *fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
|
|
};
|
|
|
|
void testConstructor() {
|
|
S s;
|
|
PrintfCtor("hello %s", s.data()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'S::data' passed as an argument to printf-like function 'PrintfCtor::PrintfCtor'}}
|
|
PrintfCtor("hello %s", s.c_str());
|
|
PrintfCtor("hello %s", getNotTerminated()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to printf-like function 'PrintfCtor::PrintfCtor'}}
|
|
PrintfCtor("hello %s", getTerminated());
|
|
}
|
|
|
|
void takesOneArg(const char *s);
|
|
void takesTwoArgs(const char *s, int n);
|
|
void takesOneVoidArg(const void *p);
|
|
|
|
struct SingleArgCtor {
|
|
explicit SingleArgCtor(const char *s);
|
|
};
|
|
|
|
struct TwoArgCtor {
|
|
explicit TwoArgCtor(const char *s, int n);
|
|
};
|
|
|
|
struct SingleVoidArgCtor {
|
|
explicit SingleVoidArgCtor(const void *p);
|
|
};
|
|
|
|
void testSingleArg() {
|
|
S s;
|
|
takesOneArg(getNotTerminated()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to single-argument function 'takesOneArg'}}
|
|
takesOneArg(getTerminated());
|
|
takesOneArg(s.data()); // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'S::data' passed as an argument to single-argument function 'takesOneArg'}}
|
|
takesOneArg(s.c_str());
|
|
takesTwoArgs(getNotTerminated(), 0);
|
|
takesOneVoidArg(getNotTerminated());
|
|
(void)mozilla::BitwiseCast<const uint8_t*>(getNotTerminated());
|
|
SingleArgCtor{getNotTerminated()}; // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'getNotTerminated' passed as an argument to single-argument function 'SingleArgCtor::SingleArgCtor'}}
|
|
SingleArgCtor{getTerminated()};
|
|
SingleArgCtor{s.data()}; // expected-error{{MOZ_NON_TERMINATED_STRING return value from 'S::data' passed as an argument to single-argument function 'SingleArgCtor::SingleArgCtor'}}
|
|
TwoArgCtor{getNotTerminated(), 0};
|
|
SingleVoidArgCtor{getNotTerminated()};
|
|
}
|
|
|
|
void testStreams() {
|
|
S s;
|
|
std::stringstream ss;
|
|
ss << s.data(); // expected-error-re{{MOZ_NON_TERMINATED_STRING return value from 'S::data' passed as an argument to operator function '{{.+}}'}}
|
|
ss << s.c_str();
|
|
}
|
|
|
|
void testStringCtor() {
|
|
S s;
|
|
std::string s1(s.data()); // expected-error-re{{MOZ_NON_TERMINATED_STRING return value from 'S::data' passed as an argument to single-argument function '{{.+}}'}}
|
|
std::string s2(s.data(), s.size());
|
|
std::string s3(s.c_str());
|
|
}
|
|
|
|
void testNonPrintfUsage() {
|
|
const char *p = getNotTerminated();
|
|
(void)p;
|
|
|
|
S s;
|
|
const char *q = s.data();
|
|
(void)q;
|
|
}
|