Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 165 additions & 12 deletions cpp/ql/lib/semmle/code/cpp/models/implementations/Printf.qll
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,29 @@ import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.NonThrowing

/**
* A formatting function that takes its format arguments through a `va_list` parameter.
*/
abstract private class VaListFormattingFunction extends FormattingFunction {
final override int getFirstFormatArgumentIndex() { none() }

final int getVaListParameterIndex() { result = this.getNumberOfParameters() - 1 }

private predicate hasLocaleParameter() { this.getName().matches("%\\_l") }

final override int getFormatParameterIndex() {
if this.hasLocaleParameter()
then result = this.getVaListParameterIndex() - 2
else result = this.getVaListParameterIndex() - 1
}
}

/**
* The standard functions `printf`, `wprintf` and their glib variants.
*/
private class Printf extends FormattingFunction, AliasFunction, NonCppThrowingFunction {
private class Printf extends FormattingFunction, AliasFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
Printf() {
this instanceof TopLevelFunction and
(
this.hasGlobalOrStdOrBslName(["printf", "wprintf"]) or
this.hasGlobalName(["printf_s", "wprintf_s", "g_printf"])
Expand All @@ -37,9 +54,9 @@ private class Printf extends FormattingFunction, AliasFunction, NonCppThrowingFu
/**
* The standard functions `fprintf`, `fwprintf` and their glib variants.
*/
private class Fprintf extends FormattingFunction, NonCppThrowingFunction {
private class Fprintf extends FormattingFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
Fprintf() {
this instanceof TopLevelFunction and
(
this.hasGlobalOrStdOrBslName(["fprintf", "fwprintf"]) or
this.hasGlobalName("g_fprintf")
Expand All @@ -52,12 +69,44 @@ private class Fprintf extends FormattingFunction, NonCppThrowingFunction {
override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = true }
}

/**
* The standard functions `vprintf`, `vwprintf` and their Microsoft variants.
*/
private class Vprintf extends VaListFormattingFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
Vprintf() {
(
this.hasGlobalOrStdOrBslName(["vprintf", "vwprintf"]) or
this.hasGlobalName(["_vprintf_l", "_vwprintf_l"])
) and
not exists(this.getDefinition().getFile().getRelativePath())
}

override predicate isOutputGlobal() { any() }
}

/**
* The standard functions `vfprintf`, `vfwprintf` and their Microsoft variants.
*/
private class Vfprintf extends VaListFormattingFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
Vfprintf() {
(
this.hasGlobalOrStdOrBslName(["vfprintf", "vfwprintf"]) or
this.hasGlobalName(["_vfprintf_l", "_vfwprintf_l"])
) and
not exists(this.getDefinition().getFile().getRelativePath())
}

override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = true }
}

/**
* The standard function `sprintf` and its Microsoft and glib variants.
*/
private class Sprintf extends FormattingFunction, NonCppThrowingFunction {
private class Sprintf extends FormattingFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
Sprintf() {
this instanceof TopLevelFunction and
(
this.hasGlobalOrStdOrBslName([
"sprintf", // sprintf(dst, format, args...)
Expand Down Expand Up @@ -95,14 +144,32 @@ private class Sprintf extends FormattingFunction, NonCppThrowingFunction {
}
}

/**
* The standard function `vsprintf` and its Microsoft variants.
*/
private class Vsprintf extends VaListFormattingFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
Vsprintf() {
(
this.hasGlobalOrStdOrBslName("vsprintf") or // vsprintf(dst, format, va_list)
this.hasGlobalName([
"_vsprintf_l", // _vsprintf_l(dst, format, locale, va_list)
"__vswprintf_l" // __vswprintf_l(dst, format, locale, va_list)
])
) and
not exists(this.getDefinition().getFile().getRelativePath())
}

override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = false }
}

/**
* Implements `Snprintf`.
*/
private class SnprintfImpl extends Snprintf, AliasFunction, SideEffectFunction,
NonCppThrowingFunction
NonCppThrowingFunction instanceof TopLevelFunction
{
SnprintfImpl() {
this instanceof TopLevelFunction and
(
this.hasGlobalOrStdOrBslName([
"snprintf", // C99 defines snprintf
Expand Down Expand Up @@ -169,15 +236,102 @@ private class SnprintfImpl extends Snprintf, AliasFunction, SideEffectFunction,
}
}

/**
* The standard function `vsnprintf`, and its Microsoft variants.
*/
private class VsnprintfImpl extends Snprintf, VaListFormattingFunction, AliasFunction,
SideEffectFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
VsnprintfImpl() {
(
this.hasGlobalOrStdOrBslName("vsnprintf") // vsnprintf(dst, count, format, va_list)
or
this.hasGlobalName([
"vsnprintf_s", // vsnprintf_s(dst, size, count, format, va_list)
"vsprintf_s", // vsprintf_s(dst, size, format, va_list)
"vswprintf_s", // vswprintf_s(dst, size, format, va_list)
"_vsnprintf", // _vsnprintf(dst, count, format, va_list)
"_vsnprintf_l", // _vsnprintf_l(dst, count, format, locale, va_list)
"_vsnprintf_s", // _vsnprintf_s(dst, size, count, format, va_list)
"_vsnprintf_s_l", // _vsnprintf_s_l(dst, size, count, format, locale, va_list)
"_vsnwprintf", // _vsnwprintf(dst, count, format, va_list)
"_vsnwprintf_l", // _vsnwprintf_l(dst, count, format, locale, va_list)
"_vsnwprintf_s", // _vsnwprintf_s(dst, size, count, format, va_list)
"_vsnwprintf_s_l", // _vsnwprintf_s_l(dst, size, count, format, locale, va_list)
"_vsprintf_p", // _vsprintf_p(dst, size, format, va_list)
"_vsprintf_p_l", // _vsprintf_p_l(dst, size, format, locale, va_list)
"_vsprintf_s_l", // _vsprintf_s_l(dst, size, format, locale, va_list)
"_vswprintf_p", // _vswprintf_p(dst, count, format, va_list)
"_vswprintf_p_l", // _vswprintf_p_l(dst, count, format, locale, va_list)
"_vswprintf_s_l" // _vswprintf_s_l(dst, size, format, locale, va_list)
])
or
this.hasGlobalOrStdOrBslName("vswprintf") and this.getNumberOfParameters() = 4
or
this.hasGlobalName("_vswprintf_l") and this.getNumberOfParameters() = 5
) and
not exists(this.getDefinition().getFile().getRelativePath())
}

override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = false }

override int getSizeParameterIndex() { result = 1 }

override predicate returnsFullFormatLength() { this.hasName(["vsnprintf", "vsnprintf_s"]) }

override predicate parameterNeverEscapes(int index) {
index =
[
this.getOutputParameterIndex(false), this.getFormatParameterIndex(),
this.getVaListParameterIndex()
]
}

override predicate parameterEscapesOnlyViaReturn(int index) { none() }

override predicate parameterIsAlwaysReturned(int index) { none() }

override predicate hasOnlySpecificReadSideEffects() { any() }

override predicate hasOnlySpecificWriteSideEffects() { any() }

override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = this.getOutputParameterIndex(false) and buffer = true and mustWrite = false
or
i = this.getVaListParameterIndex() and buffer = false and mustWrite = false
}

override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = this.getFormatParameterIndex() and buffer = true
or
i = this.getVaListParameterIndex() and buffer = false
}
}

/**
* The Microsoft `_vscprintf_p` functions and variants.
*/
private class Vscprintf extends VaListFormattingFunction, NonCppThrowingFunction instanceof TopLevelFunction
{
Vscprintf() {
this.hasGlobalName([
"_vscprintf_p", // _vscprintf_p(format, va_list)
"_vscprintf_p_l", // _vscprintf_p_l(format, locale, va_list)
"_vscwprintf_p", // _vscwprintf_p(format, va_list)
"_vscwprintf_p_l" // _vscwprintf_p_l(format, locale, va_list)
]) and
not exists(this.getDefinition().getFile().getRelativePath())
}
}

/**
* The Microsoft `StringCchPrintf` function and variants.
* See: https://learn.microsoft.com/en-us/windows/win32/api/strsafe/
* and
* https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms860435(v=msdn.10)
*/
private class StringCchPrintf extends FormattingFunction {
private class StringCchPrintf extends FormattingFunction instanceof TopLevelFunction {
StringCchPrintf() {
this instanceof TopLevelFunction and
exists(string baseName |
baseName in [
"StringCchPrintf", //StringCchPrintf(pszDest, cchDest, pszFormat, ...)
Expand Down Expand Up @@ -207,9 +361,8 @@ private class StringCchPrintf extends FormattingFunction {
/**
* The standard function `syslog`.
*/
private class Syslog extends FormattingFunction, NonCppThrowingFunction {
private class Syslog extends FormattingFunction, NonCppThrowingFunction instanceof TopLevelFunction {
Syslog() {
this instanceof TopLevelFunction and
this.hasGlobalName("syslog") and
not exists(this.getDefinition().getFile().getRelativePath())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,6 @@ private Type getAFormatterWideTypeOrDefault() {
* A standard library function that uses a `printf`-like formatting string.
*/
abstract class FormattingFunction extends ArrayFunction, TaintFunction {
int firstFormatArgumentIndex;

FormattingFunction() {
firstFormatArgumentIndex > 0 and
if this.hasDefinition()
then firstFormatArgumentIndex = this.getDefinition().getNumberOfParameters()
else
if this instanceof BuiltInFunction
then firstFormatArgumentIndex = this.getNumberOfParameters()
else
forex(FunctionDeclarationEntry fde | fde = this.getAnExplicitDeclarationEntry() |
firstFormatArgumentIndex = fde.getNumberOfParameters()
)
}

/** Gets the position at which the format parameter occurs. */
abstract int getFormatParameterIndex();

Expand Down Expand Up @@ -135,8 +120,21 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction {
* Gets the position of the first format argument, corresponding with
* the first format specifier in the format string. We ignore all
* implicit function definitions.
*
* There is no result if the formatting function takes a `va_list` argument.
*/
int getFirstFormatArgumentIndex() { result = firstFormatArgumentIndex }
int getFirstFormatArgumentIndex() {
result > 0 and
if this.hasDefinition()
then result = this.getDefinition().getNumberOfParameters()
else
if this instanceof BuiltInFunction
then result = this.getNumberOfParameters()
else
forex(FunctionDeclarationEntry fde | fde = this.getAnExplicitDeclarationEntry() |
result = fde.getNumberOfParameters()
)
}

/**
* Gets the position of the buffer size argument, if any.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,70 @@ int main(int argc, char **argv) {

return 0;
}

typedef void *va_list;
typedef void *_locale_t;

int vprintf(const char *format, va_list argptr);
int _vprintf_l(const char *format, _locale_t locale, va_list argptr);

int vfprintf(FILE *stream, const char *format, va_list argptr);
int _vfprintf_l(FILE *stream, const char *format, _locale_t locale, va_list argptr);

int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
int _vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
int _vsnprintf_l(char *buffer, size_t count, const char *format, _locale_t locale, va_list argptr);

int vsnprintf_s(
char *buffer, size_t sizeOfBuffer, size_t count, const char *format, va_list argptr
);
int _vsnprintf_s(
char *buffer, size_t sizeOfBuffer, size_t count, const char *format, va_list argptr
);
int _vsnprintf_s_l(
char *buffer, size_t sizeOfBuffer, size_t count, const char *format, _locale_t locale,
va_list argptr
);

int vsprintf(char *buffer, const char *format, va_list argptr);
int _vsprintf_l(char *buffer, const char *format, _locale_t locale, va_list argptr);

int _vsprintf_p(char *buffer, size_t sizeInBytes, const char *format, va_list argptr);
int _vsprintf_p_l(
char *buffer, size_t sizeInBytes, const char *format, _locale_t locale, va_list argptr
);

int vsprintf_s(char *buffer, size_t numberOfElements, const char *format, va_list argptr);
int _vsprintf_s_l(
char *buffer, size_t numberOfElements, const char *format, _locale_t locale, va_list argptr
);

int _vscprintf_p(const char *format, va_list argptr);
int _vscprintf_p_l(const char *format, _locale_t locale, va_list argptr);

void test() {
// BAD: User input flowing to various printf-like functions.
char fmt[1024];
char out[1024];
va_list args = 0;
_locale_t locale = 0;
fread(fmt, sizeof(char), 1024, f);
vprintf(fmt, args); // BAD
_vprintf_l(fmt, locale, args); // BAD
vfprintf(f, fmt, args); // BAD
_vfprintf_l(f, fmt, locale, args); // BAD
vsnprintf(out, 1024, fmt, args); // BAD
_vsnprintf(out, 1024, fmt, args); // BAD
_vsnprintf_l(out, 1024, fmt, locale, args); // BAD
vsnprintf_s(out, 1024, 1024, fmt, args); // BAD
_vsnprintf_s(out, 1024, 1024, fmt, args); // BAD
_vsnprintf_s_l(out, 1024, 1024, fmt, locale, args); // BAD
vsprintf(out, fmt, args); // BAD
_vsprintf_l(out, fmt, locale, args); // BAD
_vsprintf_p(out, 1024, fmt, args); // BAD
_vsprintf_p_l(out, 1024, fmt, locale, args); // BAD
vsprintf_s(out, 1024, fmt, args); // BAD
_vsprintf_s_l(out, 1024, fmt, locale, args); // BAD
_vscprintf_p(fmt, args); // BAD
_vscprintf_p_l(fmt, locale, args); // BAD
}
Loading
Loading