This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[Ping] Port of VTV for Cygwin and MinGW
- From: Patrick Wollgast <patrick dot wollgast at rub dot de>
- To: gcc-patches at gcc dot gnu dot org, libstdc++ at gcc dot gnu dot org
- Cc: cmtice at google dot com
- Date: Thu, 11 Sep 2014 06:12:47 +0200
- Subject: [Ping] Port of VTV for Cygwin and MinGW
- Authentication-results: sourceware.org; auth=none
- References: <53FF0C8C dot 8090507 at rub dot de>
Ping for https://gcc.gnu.org/ml/gcc-patches/2014-08/msg02559.html
Also added Caroline Tice, as libvtv maintainer, to cc and attached
virtual_func_test_min_UAF.cpp, which I forgot in the original mail.
Patrick
On 28.08.2014 13:03, Patrick Wollgast wrote:
> This patch contains a port of VTV -fvtable-verify=std for Cygwin and MinGW.
>
> Since weak symbols on Windows and Linux are implemented differently, and
> VTV should have the possibility to be switched on and off, the structure
> of the feature had to be modified.
> On Linux libstdc++ contains the weak stub functions of VTV. For Cygwin
> and MinGW they have been removed, due to the difference of weak symbols.
> On Linux and on Windows libstdc++ itself gets build with
> -fvtable-verify=std. Since libvtv gets build after libstdc++, and
> libstdc++ doesn't contain the stub functions any more, 'undefined
> reference' errors are thrown during linking of libstdc++. To prevent
> these errors during the linking process a libvtv-0.dll gets build from
> the stub functions before libstdc++-6.dll is linked.
> At the end of the build process two VTV dlls have been build. One is
> called libvtv-0.dll, containing the real functions, the other is called
> libvtv_stubs-0.dll, containing the stub functions. Depending on whether
> libvtv-0.dll is first found in the dll search path or
> libvtv_stubs-0.dll, renamed to libvtv-0.dll, the real functions or the
> stub functions are used.
>
> Testing:
> The test builds were configured the following way:
> Linux 64bit (from patched and unpatched trunk):
> /path/to/configure --prefix=/prefix/gcc-vtv-bin-64
> --enable-libstdcxx-threads --enable-vtable-verify=yes
> MinGW 32bit cross compiled:
> /path/to/configure --target=i686-w64-mingw32
> --prefix=/prefix/mingw-vtv-bin-32 --with-gnu-ld --with-gnu-as
> --enable-fully-dynamic-string --disable-multilib
> --enable-libstdcxx-threads --enable-vtable-verify=yes
> MinGW 64bit cross compiled:
> /path/to/configure --target=x86_64-w64-mingw32
> --prefix=/prefix/mingw-vtv-bin-64 --with-gnu-ld --with-gnu-as
> --enable-fully-dynamic-string --disable-multilib
> --enable-libstdcxx-threads --enable-vtable-verify=yes
> Cygwin 64bit:
> /path/to/configure --enable-languages=c,c++ --enable-libstdcxx-threads
> --enable-vtable-verify=yes
>
> At Linux the patched and unpatched version resulted in the same number
> of passed tests with 'make check-target-libvtv'.
>
> Since MinGW was cross compiled the test cases couldn't be built and run
> with 'make check-target-libvtv'. Therefore they were built with the
> attached makefiles and tested afterwards on Windows 7 64bit. Some test
> cases contain Linux specific parts and weren't tested. See the makefiles
> for further information. Additionally virtual_func_test_min_UAF.cpp was
> also built and tested. All built tests passed.
>
> Cygwin was just tested on gcc 4.9.0, because the current trunk isn't
> building for me. Even the clean trunk without the patch attached to this
> mail. On Cygwin with gcc 4.9.0 VTV worked.
>
> Besides the test cases Botan was also built and tested (gcc 4.9.0) with
> MinGW 32bit and VTV.
>
> regards
>
--
Beste GrÃÃe,
Patrick
#include<iostream>
#include<stdio.h>
/*
* Demonstrates an use after free c++ internally by deleting an object
* and resetting its vtable pointing to legitimate functions
*/
// set pointer size and integer size dependent on architechture
#if __x86_64__
/* 64-bit */
const char* arch = "x86_64";
int ptrSize = 8;
typedef long long myInt; // 8 byte
#else
/* 32-bit */
const char* arch = "x86";
int ptrSize = 4;
typedef int myInt; // 4 byte
#endif
//
using namespace std;
// BASE CLASS
class Addition{
protected:
// member variables
int s1,s2;
public:
// virtual member function will be implemented in derived class
virtual int add(int a,int b){
};
int i;
};
//
// inherit new class
class Add: public Addition{
public:
// constructor:
/*
Add(int a, int b){
s1 = a;
s2 = b;
};
*/
void setvals(int a, int b){
s1 = a;
s2 = b;
}
// implement virtual function
virtual int TRIGGER(){
cout<<s1 + s2<<"\n";
};
int j;
};
//
// use this function in vtable which will be injected
void inject(){
printf("%s\n","I've been executed");
}
int main(){
printf("We're on a %s architecture\n\n",arch);
// create object of inherited class
//Add* A = new Add(4,5);
Add* A = new Add; //heap allocation happens here
A->setvals(4,5);
// add attributes
A->i = 6;
A->j = 7;
// get vtable address
myInt vtablePTR = NULL;
vtablePTR = *((myInt*)A);
printf("vtable: %.16x\n", vtablePTR);
//function 1
printf("%.16x : %.16x\n", vtablePTR, *((myInt*)vtablePTR));
//function 2
printf("%.16x : %.16x\n", vtablePTR + ptrSize, *((myInt*)(vtablePTR + ptrSize)));
// call instantiated virtual function (legitimate)
A->TRIGGER();
// FREE OBJECT !
delete A;
//A = NULL; // this prevents use after free
// get objects address as it is still valid
myInt* ObjPTR = (myInt*)A;
// let object point to old vtable (inject old vtable)
*ObjPTR = vtablePTR;
// NOT legitimate
A->TRIGGER();
myInt vtable_func = (myInt)inject;
// create new vtable
myInt vtable_new[2] = {vtable_func, vtable_func};
// let object point to new vtable (inject new vtable)
*ObjPTR = (myInt)vtable_new;
// USE AFTER FREE (execute injected function in vtable)
A->TRIGGER();
__asm__(
"nop;"
"nop;"
"nop;"
);
// USE AFTER FREE with data members
printf("%i %i\n", A->i, A->j);
}