Semmle 1.20
Skip to end of metadata
Go to start of metadata

Name: Unbounded write

Description: Buffer write operations that do not control the length of data written may overflow.

ID: cpp/unbounded-write

Kind: problem

Severity: error

Precision: medium

Query: UnboundedWrite.ql
/**
 * @name Unbounded write
 * @description Buffer write operations that do not control the length
 *              of data written may overflow.
 * @kind problem
 * @problem.severity error
 * @precision medium
 * @id cpp/unbounded-write
 * @tags reliability
 *       security
 *       external/cwe/cwe-120
 *       external/cwe/cwe-787
 *       external/cwe/cwe-805
 */
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking

// --- Summary of CWE-120 violations ---
//
// The essence of CWE-120 is that string / buffer copies that are
// potentially unbounded, e.g. null terminated string copy,
// should be controlled e.g. by using strncpy instead of strcpy.
// In practice this is divided into several queries that
// handle slightly different sub-cases, exclude some acceptable uses,
// and produce reasonable messages to fit each issue.
//
// cases:
//    hasExplicitLimit()    exists(getMaxData())  exists(getBufferSize(bw.getDest(), _))) handled by
//    NO                    NO                    either                                      UnboundedWrite.ql isUnboundedWrite()
//    NO                    YES                   NO                                          UnboundedWrite.ql isMaybeUnboundedWrite()
//    NO                    YES                   YES                                         OverrunWrite.ql, OverrunWriteFloat.ql
//    YES                   either                YES                                         BadlyBoundedWrite.ql
//    YES                   either                NO                                          (assumed OK)

// --- CWE-120UnboundedWrite ---

predicate isUnboundedWrite(BufferWrite bw) {
  not bw.hasExplicitLimit()                           // has no explicit size limit
  and (not exists(bw.getMaxData()))                   // and we can't deduce an upper bound to the amount copied
}

/*predicate isMaybeUnboundedWrite(BufferWrite bw)
{
  not bw.hasExplicitLimit()                           // has no explicit size limit
  and exists(bw.getMaxData())                         // and we can deduce an upper bound to the amount copied
  and (not exists(getBufferSize(bw.getDest(), _)))    // but we can't work out the size of the destination to be sure
}*/

// --- user input reach ---

/**
 * Identifies expressions that are potentially tainted with user
 * input.  Most of the work for this is actually done by the
 * TaintTracking library.
 */
predicate tainted2(Expr expr, Expr inputSource, string inputCause) {
  (
    taintedIncludingGlobalVars(inputSource, expr, _) and
    inputCause = inputSource.toString()
  ) or exists(Expr e | tainted2(e, inputSource, inputCause) |
    // field accesses of a tainted struct are tainted
    e = expr.(FieldAccess).getQualifier()
  )
}

// --- put it together ---

from BufferWrite bw, Expr inputSource, string inputCause
where isUnboundedWrite(bw)
  and tainted2(bw.getASource(), inputSource, inputCause)
select bw,  "This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.", inputSource, inputCause

The program performs a buffer copy or write operation with no upper limit on the size of the copy. An unexpectedly long input that reaches this code will cause the buffer to overflow. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.

Recommendation

Always control the length of buffer copy and buffer write operations. strncpy should be used over strcpy, snprintf over sprintf etc. In general 'n-variant' functions should be preferred.

Example

void congratulateUser(const char *userName)
{
	char buffer[80];

	// BAD: this could overflow the buffer if the UserName is long
	sprintf(buffer, "Congratulations, %s!", userName);

	MessageBox(hWnd, buffer, "New Message", MB_OK);
}

In this example, the call to sprintf may overflow buffer. This occurs if the argument userName is very long, such that the resulting string is more than the 80 characters allowed.

To fix the problem the call to sprintf should be replaced with snprintf, specifying a maximum length of 80 characters.

References