Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Libtorrent crashes while parsing invalid chunked HTTP or UPnP response #780

Closed
brandonprry opened this issue Jun 3, 2016 · 7 comments
Closed

Comments

@brandonprry
Copy link

libtorrent version (or branch): 1.1

platform/architecture: Linux x86_64

compiler and compiler version: Ubuntu clang version 3.6.2-1 (tags/RELEASE_362/final) (based on LLVM 3.6.2)


A specially crafted HTTP response from a tracker (or potentially a UPnP broadcast) can crash libtorrent in the parse_chunk_header() function. This bug was found with AFL. Let me know if you need any more information or cannot reproduce.

Base64-encoded HTTP response that should crash libtorrent:

SG9UUC8xLjEgMjAwIE9LDQpDb250LUxlbmd0aDogMjANCkNvbnRlbnRlOiB0bg0KVHJhbnNmZXIt
RW5jb2Rpbmc6IGNodW5rZWQNCg0NCg0KCi04OWFiYzlhYmNkZWYNCtYNClS9vb29vb29vb29ZGVm
DQrWDQpUvb29vb29vb29vb1lc3QtaGVhZHlyOiBmb29iYXINCg0K

ASan output:

ASAN:SIGSEGV
=================================================================
==10703==ERROR: AddressSanitizer: SEGV on unknown address 0xffffd734365511c3 (pc 0x0000004e6dd2 bp 0x60e00000dff9 sp 0x7ffc46732130 T0)
    #0 0x4e6dd1 in char const* std::__find_if<char const*, __gnu_cxx::__ops::_Iter_equals_val<char const> >(char const*, char const*, __gnu_cxx::__ops::_Iter_equals_val<char const>, std::random_access_iterator_tag) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/bits/stl_algo.h:113:19
    #1 0x4e6dd1 in char const* std::__find_if<char const*, __gnu_cxx::__ops::_Iter_equals_val<char const> >(char const*, char const*, __gnu_cxx::__ops::_Iter_equals_val<char const>) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/bits/stl_algo.h:161
    #2 0x4e6dd1 in char const* std::find<char const*, char>(char const*, char const*, char const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/bits/stl_algo.h:3790
    #3 0x4e6dd1 in libtorrent::http_parser::parse_chunk_header(libtorrent::buffer::const_interval, long*, int*) /root/libtorrent-libtorrent-1_1/src/http_parser.cpp:432
    #4 0x4e60e8 in libtorrent::http_parser::incoming(libtorrent::buffer::const_interval, bool&) /root/libtorrent-libtorrent-1_1/src/http_parser.cpp:348:10
    #5 0x4e013e in main /root/http_fuzz.c:38:1
    #6 0x7f242c6bfabf in __libc_start_main /build/glibc-qbmteM/glibc-2.21/csu/libc-start.c:289
    #7 0x438c98 in _start (/root/http_fuzz_asan+0x438c98)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/bits/stl_algo.h:113 char const* std::__find_if<char const*, __gnu_cxx::__ops::_Iter_equals_val<char const> >(char const*, char const*, __gnu_cxx::__ops::_Iter_equals_val<char const>, std::random_access_iterator_tag)
==10703==ABORTING

Valgrind output:

==20278== Memcheck, a memory error detector
==20278== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==20278== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==20278== Command: /root/http_fuzz id:000042,sig:11,src:000127+000181,op:splice,rep:2
==20278==
==20278== Invalid read of size 1
==20278==    at 0x409892: operator()<const char *> (predefined_ops.h:194)
==20278==    by 0x409892: __find_if<const char *, __gnu_cxx::__ops::_Iter_equals_val<const char> > (stl_algo.h:120)
==20278==    by 0x409892: __find_if<const char *, __gnu_cxx::__ops::_Iter_equals_val<const char> > (stl_algo.h:161)
==20278==    by 0x409892: find<const char *, char> (stl_algo.h:3790)
==20278==    by 0x409892: libtorrent::http_parser::parse_chunk_header(libtorrent::buffer::const_interval, long*, int*) (http_parser.cpp:432)
==20278==    by 0x408BA8: libtorrent::http_parser::incoming(libtorrent::buffer::const_interval, bool&) (http_parser.cpp:348)
==20278==    by 0x403BE7: main (http_fuzz.c:38)
==20278==  Address 0xffff76543c621153 is not stack'd, malloc'd or (recently) free'd
==20278==
==20278==
==20278== Process terminating with default action of signal 11 (SIGSEGV)
==20278==  General Protection Fault
==20278==    at 0x409892: operator()<const char *> (predefined_ops.h:194)
==20278==    by 0x409892: __find_if<const char *, __gnu_cxx::__ops::_Iter_equals_val<const char> > (stl_algo.h:120)
==20278==    by 0x409892: __find_if<const char *, __gnu_cxx::__ops::_Iter_equals_val<const char> > (stl_algo.h:161)
==20278==    by 0x409892: find<const char *, char> (stl_algo.h:3790)
==20278==    by 0x409892: libtorrent::http_parser::parse_chunk_header(libtorrent::buffer::const_interval, long*, int*) (http_parser.cpp:432)
==20278==    by 0x408BA8: libtorrent::http_parser::incoming(libtorrent::buffer::const_interval, bool&) (http_parser.cpp:348)
==20278==    by 0x403BE7: main (http_fuzz.c:38)
==20278==
==20278== HEAP SUMMARY:
==20278==     in use at exit: 73,716 bytes in 7 blocks
==20278==   total heap usage: 12 allocs, 5 frees, 73,819 bytes allocated
==20278==
==20278== LEAK SUMMARY:
==20278==    definitely lost: 0 bytes in 0 blocks
==20278==    indirectly lost: 0 bytes in 0 blocks
==20278==      possibly lost: 0 bytes in 0 blocks
==20278==    still reachable: 73,716 bytes in 7 blocks
==20278==         suppressed: 0 bytes in 0 blocks
==20278== Rerun with --leak-check=full to see details of leaked memory
==20278==
==20278== For counts of detected and suppressed errors, rerun with: -v
==20278== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
@brandonprry
Copy link
Author

Also, fwiw, the example program I used to fuzz this is:

#include "libtorrent/upnp.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/socket_io.hpp" // print_endpoint
#include <fstream>
#include <functional>
#include <boost/ref.hpp>
#include <boost/smart_ptr.hpp>
#include <iostream>

int main (int argc, char* argv[]){
  using namespace libtorrent;
  namespace lt = libtorrent;

  FILE *fp;
  long lSize;
  char *buffer;

  while (__AFL_LOOP(1000)){
    fp = fopen ( argv[1] , "rb" );
    if( !fp ) perror("blah.txt"),exit(1);

    fseek( fp , 0L , SEEK_END);
    lSize = ftell( fp );
    rewind( fp );

    /* allocate memory for entire content */
    buffer = (char*)calloc( 1, lSize+1 );
    if( !buffer ) fclose(fp),fputs("memory alloc fails",stderr),exit(1);

    /* copy the file into the buffer */
    if( 1!=fread( buffer , lSize, 1 , fp) )
      fclose(fp),free(buffer),fputs("entire read fails",stderr),exit(1);


    http_parser p;
    bool error = false;

    p.incoming(buffer::const_interval(buffer, buffer+lSize), error);


    fclose(fp);
    free(buffer);   
  }
}

Compiled with:

afl-clang-fast++ http_fuzz.c -I libtorrent-libtorrent-1_1/src/ ~/libtorrent-libtorrent-1_1/src/.libs/libtorrent-rasterbar.a -lboost_system -lboost_random -o http_fuzz

@aldenml
Copy link
Contributor

aldenml commented Jun 3, 2016

I'm not familiar with Valgrind, but for what I can see in the log, the library is using an specialized algorithm tailored to char*, if that's the case then the message Invalid read of size 1 tells me that it's checking for NULL terminated strings.

Is this clang using libstd++ instead libc++?

@brandonprry
Copy link
Author

brandonprry commented Jun 3, 2016

I am not sure what you mean by a "specialized algorithm tailored to char *".

I am linked to libstdc++

root@w00den-fuzzer:~# ldd http_fuzz
    linux-vdso.so.1 =>  (0x00007fff9b1f6000)
    libboost_system.so.1.58.0 => /usr/lib/x86_64-linux-gnu/libboost_system.so.1.58.0 (0x00007f1b9157a000)
    libboost_random.so.1.58.0 => /usr/lib/x86_64-linux-gnu/libboost_random.so.1.58.0 (0x00007f1b91373000)
    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1b90ff0000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f1b90ce8000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1b90ad1000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1b90706000)
    /lib64/ld-linux-x86-64.so.2 (0x000055c328a1d000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1b904e8000)

@brandonprry
Copy link
Author

brandonprry commented Jun 3, 2016

If you want to compile the C program I provided without AFL, you should remove the while (__AFL_LOOP(1000)){}

It should compile with just clang then.

Also, the line that is failing is 432 in http_parser.cpp:

char const* newline = std::find(pos, buf.end, '\n');

@arvidn
Copy link
Owner

arvidn commented Jun 4, 2016

Thanks! fix for RC_1_1 is here. #782 I will back-port it to RC_1_0 once this passes CI

@arvidn arvidn closed this as completed Jun 4, 2016
@bendikro
Copy link

@arvidn Does that mean this will go into a v1.0.10?

@arvidn
Copy link
Owner

arvidn commented Jul 12, 2016

yes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants