The fread() function, as defined in the C Standard, subclause 7.21.8.1 [ISO/IEC 9899:2011], does not explicitly null-terminate the read character sequence.

Synopsis
size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream)

Description
The fread function reads, into the array pointed to by ptr, up to nmemb elements
whose size is specified by size, from the stream pointed to by stream.

Although the content of a file has a properly null-terminated character sequence, if nmemb is less than the total length of the characters, the fread() function will not read after nmemb characters. fread() will not append a null character to the end of the string being read to.

Noncompliant Code Example

Suppose we have a null-terminated character sequence in a file, and we need to extract a null-terminated byte string:

#include <stdio.h>
#include <stdlib.h>

int main (void) {

    FILE *fp;
    size_t size;
    long length;
    char *buffer;

    fp = fopen("file.txt", "rb");

    if (fp == NULL) {
      /* Handle file open error */
    }

    /* Obtain file size */
    if (fseek(fp, 0, SEEK_END) != 0) {
      /* Handle repositioning error */
    }

    length = ftell(fp);

    if (fseek(fp, 0L, SEEK_SET) != 0) {
      /* Handle repositioning error */
    }

    /* Allocate memory to contain whole file */
    buffer = (char*) malloc(length);
    if (buffer == NULL) {
      /* Handle memory allocation error */
    }
    
    /* size assigned here in some other code */

    if (fread(buffer, 1, size, fp) < size) {
      /* Handle file read error */
    }
    fclose(fp);

    return 0;
}

When size is less than the total length of the file (file.txt), buffer is not properly null-terminated.

Compliant Solution

To correct this example, the size of buffer must be compared with the total length of the file to identify the erroneous case where size differs from length. At this point, it is up to the programmer to handle this case.

#include <stdio.h>
#include <stdlib.h>

int main (void) {

    FILE *fp;
    size_t size;
    long length;
    char *buffer;

    fp = fopen("file.txt", "rb");

    if (fp == NULL) {
      /* Handle file open error */
    }

    /* Obtain file size */
    if (fseek(fp, 0, SEEK_END) != 0) {
      /* Handle repositioning error */
    }
    length = ftell(fp);

    if (fseek(fp, 0L, SEEK_SET) != 0) {
      /* Handle repositioning error */
    }

    /* Allocate memory to contain whole file */
    buffer = (char*) malloc(length);
    if (buffer == NULL) {
      /* Handle memory allocation error */
    }

    /* ... Assign size here ... */
    if (length != size) {
      /* Handle case when size isn't the length of file */
    }
    /* ... Other code ... */

    if (fread(buffer, 1, size, fp) < size) {
      /* Handle file read error */
    }

    fclose(fp);

    return 0;
}

Risk Assessment

When reading an input stream, the read character sequence is not explicitly null-terminated by the fread() function. Operations on the read-to buffer could result in overruns, causing abnormal program termination.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO17-C

Low

Likely

Medium

P6

L2

Automated Detection

Tool

Version

Checker

Description

LDRA tool suite
44 SEnhanced enforcement

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this guideline on the CERT website.

Related Guidelines

SEI CERT C++ Coding StandardVOID FIO20-CPP. Do not rely on an ending null character when using read()

Bibliography

[ISO/IEC 9899:2011]Subclause 7.21.8.1, "The fread Function"