pnggroup/libpng

Crash in png_convert_from_time_t (libpng1.6.37)

thientc opened this issue ยท 5 comments

Hello libpng team,

This bug was found by FUTAG - a program for automated generating fuzz-targets of libraries (a product of Ivannikov Institute for System Programming of the Russian Academy of Sciences - https://www.ispras.ru/). Thanks to following colleagues: Tran Chi Thien (thientc@ispras.ru) and Shamil Kurmangaleev(kursh@ispras.ru).

Product version: libpng1.6.37
Environment: Ubuntu 18.04
Reprocedure:
Compile fuzz-target generated by FUTAG with libFuzzer:

//fuzz-target of png_convert_from_time_t
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include<png.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if(Size < sizeof(png_time) + sizeof(time_t)) return 0;
uint8_t * pos = (uint8_t *) Data;

png_timep ptime = (png_timep) malloc(sizeof(png_time));
memset( ptime, 0, sizeof(ptime));
memcpy(ptime, pos, sizeof(png_time));
pos += sizeof(png_time);

time_t ttime = {0};
memcpy(&ttime, pos, sizeof(time_t)); 

png_convert_from_time_t(ptime, ttime);
free( (png_timep) ptime);
return 0;

}

Compile script:

clang++ -g -fsanitize=fuzzer,undefined,address png_convert_from_time_t.cc libpng16.so

AddressSanitizer Debug result:

AddressSanitizer:DEADLYSIGNAL

==28273==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000014 (pc 0x7f44491d3860 bp 0x7ffdb5d18a30 sp 0x7ffdb5d18898 T0)
==28273==The signal is caused by a READ memory access.
==28273==Hint: address points to the zero page.
#0 0x7f44491d3860 in png_convert_from_struct_tm /home/futag/libs4test/libpng-1.6.37/pngwrite.c:476
#1 0x7f44491d38b1 in png_convert_from_time_t /home/futag/libs4test/libpng-1.6.37/pngwrite.c:492
#2 0x546d72 in LLVMFuzzerTestOneInput /home/futag/install_libs/libpng/___png_convert_from_time_t_png_timep_time_t/png_convert_from_time_t.cc:19:5
#3 0x444dc6 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /home/futag/futag/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:562
#4 0x44c7f8 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) /home/futag/futag/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:472
#5 0x44e14c in fuzzer::Fuzzer::MutateAndTestOne() /home/futag/futag/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:709
#6 0x450ee7 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocatorfuzzer::SizedFile >&) /home/futag/futag/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:846
#7 0x435ce9 in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) /home/futag/futag/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:900
#8 0x41ed92 in main /home/futag/futag/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20
#9 0x7f4448201bf6 in __libc_start_main /build/glibc-S9d2JN/glibc-2.27/csu/../csu/libc-start.c:310
#10 0x41ede9 in _start (/home/futag/install_libs/libpng/___png_convert_from_time_t_png_timep_time_t/a.out+0x41ede9)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/futag/libs4test/libpng-1.6.37/pngwrite.c:476 in png_convert_from_struct_tm
==28273==ABORTING

Analysis:
Function png_convert_from_time_t call function png_convert_from_struct_tm, in which lack of checking input value ttime->tm_year:

ptime->year = (png_uint_16)(1900 + ttime->tm_year);

The code in question calls gmtime with your time_t value which seems to be just a zero. It looks like your gmtime then returned a NULL pointer. I looked at the gmtime manual on FreeBSD but it doesn't note that gmtime can return a null pointer.

  • What operating system are you using?
  • Is the NULL return value of gmtime documented in the manual page of your OS?
  • If you remove all of the code except the following lines,
time_t ttime = {0};
memcpy(&ttime, pos, sizeof(time_t)); 
png_convert_from_time_t(ptime, ttime);

does the error still occur?

  • What operating system are you using?

It's in the first post: Ubuntu 18.04

  • Is the NULL return value of gmtime documented in the manual page of your OS?

http://manpages.ubuntu.com/manpages/bionic/man3/ctime.3.html

The gmtime() function converts the calendar time timep to broken-down time representation,
       expressed  in Coordinated Universal Time (UTC).  It may return NULL when the year does not
       fit into an integer. 

png_convert_from_time_t(png_timep ptime, time_t ttime)
{
struct tm *tbuf;

png_debug(1, "in png_convert_from_time_t");

tbuf = gmtime(&ttime);
png_convert_from_struct_tm(ptime, tbuf);
}

In png_convert_from_time_t, gmtime returns NULL, this dues to crash in next fuction png_convert_from_struct_tm

It looks like there needs to be a check for NULL pointers within png_convert_from_time_t for the sake of Ubuntu. Is it possible you can supply the value of pos here so that we have a way to reproduce the bug on Ubuntu systems? It's not clear what value of LLVMFuzzerTestOneInput caused the crash.

You can get these values to test on Ubuntu:

png_timep ptime;
ptime->year = 0;
ptime->month = 0;
ptime->day = 0;
ptime->hour = 0;
ptime->minute = 0;
ptime->second = 0;
time_t ttime = 8319119876378817395;
png_convert_from_time_t(ptime, ttime);

png_convert_from_time_t.zip

Fixed in the master branch. Apologies for the delay, and many thanks for your report!