gcc FORTIFY_SOURCE drastically increases binary size

915 Views Asked by At

We have a very large C++ codebase that we would like to compile using gcc with the "FORTIFY_SOURCE=2" option to improve security and reduce the risk of buffer overflows. The problem is when we compile the system using FORTIFY_SOURCE, the binary sizes drastically increase. (From a total of 4GB to over 25GB) This causes issues when we need to deploy the code because it takes 5x as long to zip it up and deploy it.

In an attempt to figure out what was going on, I made a simple test program that does a bunch of string copies with strcpy (one of the functions FORTIFY_SOURCE is supposed to enhance and compiled it both with and without "FORTIFY_SOURCE".

#include <cstring>
#include <iostream>

using namespace std;

int main()
{
    char buf1[100];
    char buf2[100];
    char buf3[100];
    char buf4[100];
    char buf5[100];
    char buf6[100];
    char buf7[100];
    char buf8[100];
    char buf9[100];
    char buf10[100];

    strcpy(buf1, "this is a string");
    strcpy(buf2, "this is a string");
    strcpy(buf3, "this is a string");
    strcpy(buf4, "this is a string");
    strcpy(buf5, "this is a string");
    strcpy(buf6, "this is a string");
    strcpy(buf7, "this is a string");
    strcpy(buf8, "this is a string");
    strcpy(buf9, "this is a string");
    strcpy(buf10, "this is a string");
}

Compilation:

g++ -o main -O3 fortify_test.cpp

and

g++ -o main -D_FORTIFY_SOURCE=2 -O3 fortify_test.cpp

I discovered that using "FORTIFY_SOURCE" on a simple example had no noticeable impact on binary size (the resulting binary was 8.4K with and without fortifying the source.)

When there's no noticeable impact with a simple example, I wouldn't expect to see such a drastic size increase in more complex examples. What could FORTIFY_SOURCE possibly be doing to increase our binary sizes so drastically?

1

There are 1 best solutions below

0
On

Your example is actually a not very good one because there's no fortifiable code on it. Code fortification is not magical, and the compiler can only do it under some specific conditions.

Lets take a sample of code with 2 functions, one can be fortified by the compiler (because from the code itself it can determine the maximum size of the buffer), the other cannot (because same information is missing):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>

int f_protected(char *in)
{
    char buffer[256];     
    memcpy(buffer, in, strlen(in));
    printf("Hello %s !\n", buffer);
    return 0;
}

int f_not_protected(char *in, int sz)
{
    char buffer[sz];
    memcpy(buffer, in, strlen(in));
    printf("Hello %s !\n", buffer);
    return 0;
}

int main (int argc, char **argv, char **envp)
{
    if(argc < 2){
        printf("Usage: %s <some string>\n", argv[0]);
        exit(EXIT_SUCCESS);
    }
    
    f_protected(argv[1]);
    f_not_protected(argv[1], strlen(argv[1]));
    return 0;
}

There's an amazing online tool that allows you compared compiled code at https://godbolt.org/

You can actually compare both compiled versions of this sample here.

As you will be able to see in the ASM output, the fortified version of this function does perform more checks than the unfortified one, requiring extra ASM code, actually increasing file size.

However, it's hard to think of a case where it would increment code size so much. Is it possible that maybe you're not stripping debug info?