diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll index b9f320e57b2..1cf45439d8b 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll @@ -756,9 +756,9 @@ private predicate modeledFlowBarrier(Node n) { partialFlowFunc = call.getStaticCallTarget() and not partialFlowFunc.isPartialWrite(output) | - call.getStaticCallTarget().(DataFlow::DataFlowFunction).hasDataFlow(_, output) + partialFlowFunc.(DataFlow::DataFlowFunction).hasDataFlow(_, output) or - call.getStaticCallTarget().(Taint::TaintFunction).hasTaintFlow(_, output) + partialFlowFunc.(Taint::TaintFunction).hasTaintFlow(_, output) ) or exists(Operand operand, Instruction instr, Node n0, int indirectionIndex | diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FormattingFunction.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FormattingFunction.qll index ce65a65319a..757db13fe8c 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FormattingFunction.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FormattingFunction.qll @@ -170,6 +170,16 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction { output.isParameterDeref(this.getOutputParameterIndex(_)) ) } + + final override predicate isPartialWrite(FunctionOutput output) { + exists(int outputParameterIndex | + output.isParameterDeref(outputParameterIndex) and + // We require the output to be a stream since that definitely means that + // it's a partial write. If it's not a stream then it will most likely + // fill the whole buffer. + outputParameterIndex = this.getOutputParameterIndex(true) + ) + } } /** diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index e19f34eb217..76eaebb52cd 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -7767,6 +7767,10 @@ WARNING: module 'TaintTracking' has been deprecated and may be removed in future | taint.cpp:830:20:830:34 | call to indirect_source | taint.cpp:832:23:832:24 | in | | | taint.cpp:831:15:831:17 | out | taint.cpp:832:18:832:20 | out | | | taint.cpp:831:15:831:17 | out | taint.cpp:833:8:833:10 | out | | +| taint.cpp:841:21:841:35 | call to indirect_source | taint.cpp:842:11:842:12 | fp | | +| taint.cpp:841:21:841:35 | call to indirect_source | taint.cpp:843:16:843:17 | fp | | +| taint.cpp:842:11:842:12 | ref arg fp | taint.cpp:843:16:843:17 | fp | | +| taint.cpp:842:15:842:16 | | taint.cpp:842:11:842:12 | ref arg fp | TAINT | | thread.cpp:10:27:10:27 | s | thread.cpp:10:27:10:27 | s | | | thread.cpp:10:27:10:27 | s | thread.cpp:11:8:11:8 | s | | | thread.cpp:14:26:14:26 | s | thread.cpp:15:8:15:8 | s | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index 74bcf26ea7d..0c09665de1c 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -831,4 +831,15 @@ void test_write_to_const_ptr_ptr() { const char* out; take_const_ptr(out, in); sink(out); // $ SPURIOUS: ast +} + +void indirect_sink(FILE *fp); +int fprintf(FILE *fp, const char *format, ...); + +int f7(void) +{ + FILE* fp = (FILE*)indirect_source(); + fprintf(fp, ""); + indirect_sink(fp); // $ ir MISSING: ast + return 0; } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql index f5f483cdf1b..3bde6ca03f0 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.ql @@ -117,6 +117,11 @@ module IRTest { call.getTarget().getName() = "sink" and [sink.asExpr(), sink.asIndirectExpr()] = call.getAnArgument() ) + or + exists(FunctionCall call | + call.getTarget().getName() = "indirect_sink" and + sink.asIndirectExpr() = call.getAnArgument() + ) } predicate isBarrier(DataFlow::Node barrier) { diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected index 239ed2ec607..f5342ea7694 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected @@ -17670,6 +17670,55 @@ signatureMatches | taint.cpp:822:6:822:19 | take_const_ptr | (unsigned long *,const char *) | | set_cert_ex | 1 | | taint.cpp:822:6:822:19 | take_const_ptr | (unsigned long *,const char *) | | set_name_ex | 1 | | taint.cpp:822:6:822:19 | take_const_ptr | (uv_pipe_t *,const char *) | | uv_pipe_bind | 1 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_default_uflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_feof | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_ferror | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_file_close_mmap | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_file_underflow_mmap | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_ftell | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_getc | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_getwc | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_new_file_underflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_peekc_locked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_str_count | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_str_underflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_sungetc | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_sungetwc | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_wdefault_uflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_wfile_underflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_wfile_underflow_mmap | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_wstr_count | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | _IO_wstr_underflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __fbufsize | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __feof_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __ferror_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __fileno | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __flbf | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __fopen_maybe_mmap | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __fpending | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __ftello | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __fwriting | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __getc_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __getwc_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __uflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __underflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __wuflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | __wunderflow | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | feof_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | ferror_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | fgetc_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | fgetgrent | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | fgetpwent | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | fgetsgent | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | fgetspent | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | getc_unlocked | 0 | +| taint.cpp:836:6:836:18 | indirect_sink | (FILE *) | | getmntent | 0 | +| taint.cpp:837:5:837:11 | fprintf | (CURLSH *,CURLSHoption,...) | | curl_share_setopt | 2 | +| taint.cpp:837:5:837:11 | fprintf | (Jim_Interp *,const char *,...) | | Jim_SetResultFormatted | 1 | +| taint.cpp:837:5:837:11 | fprintf | (Jim_Interp *,const char *,...) | | Jim_SetResultFormatted | 2 | +| taint.cpp:837:5:837:11 | fprintf | (char **,const char *,...) | | ___asprintf | 1 | +| taint.cpp:837:5:837:11 | fprintf | (char **,const char *,...) | | ___asprintf | 2 | +| taint.cpp:837:5:837:11 | fprintf | (curl_httppost **,curl_httppost **,...) | | curl_formadd | 2 | | thread.cpp:4:6:4:9 | sink | (int) | | ASN1_STRING_type_new | 0 | | thread.cpp:4:6:4:9 | sink | (int) | | ASN1_tag2bit | 0 | | thread.cpp:4:6:4:9 | sink | (int) | | ASN1_tag2str | 0 | @@ -47191,6 +47240,10 @@ getParameterTypeName | taint.cpp:817:6:817:27 | write_to_const_ptr_ptr | 1 | const char ** | | taint.cpp:822:6:822:19 | take_const_ptr | 0 | const char * | | taint.cpp:822:6:822:19 | take_const_ptr | 1 | const char * | +| taint.cpp:836:6:836:18 | indirect_sink | 0 | FILE * | +| taint.cpp:837:5:837:11 | fprintf | 0 | FILE * | +| taint.cpp:837:5:837:11 | fprintf | 1 | const char * | +| taint.cpp:837:5:837:11 | fprintf | 2 | ... | | thread.cpp:4:6:4:9 | sink | 0 | int | | thread.cpp:6:8:6:8 | operator= | 0 | S && | | thread.cpp:6:8:6:8 | operator= | 0 | const S & |