How to ban use of some functions in C and C++?


Reading time: 30 minutes | Coding time: 10 minutes

One approach to prevent the use of functions is to replace the function definition (using macros) with a dummy definition with a message that the function is banned from use. Such macro definitions need to be defined in a header file (say "banned.h") and include this header file in each source code after all other header files.

We covered the following sections:

  • What does banning a function mean?
  • Why we need to ban some functions?
  • How to ban a function?
  • Real life example (in Git)

Prerequisites:

What does banning a function mean?

Banning a function means that the function cannot be used in the program even if you have imported the relavant libraries and implement its use correctly. It should give a compilation error.

For example, we want ban the function strcpy() which is a part of the library string.h. So, if we include the relavant library and use the function correctly, it will work but we want it to fail.

Consider this code:

#include<stdio.h> 
#include<string.h> 

int main () 
{ 
    char str1[]="opengenus"; 
    char str2[] = "some data"; 
    
    strcpy(str2, str1); 
    printf ("str1: %s\nstr2: %s\n", str1, str2); 
    return 0; 
}

This code works correctly. Our goal is to make the compilation of this code to fail to make sure that:

  • the programmer is forced to change the implementation
  • strcpy never makes into the source code

We will solve this with our bullet proof approach.

Why we need to ban some functions?

C and C++ are old languages and over the years, some functions have proved to be dangerous in terms of security and application consistency. Though every language features can be used safely with restrictions but it has been seen programmers have frequently missed used these functions.

Some functions which one shall ban in C/C++ are:

  • strcpy(): This function expects the string to be null terminated. In general, strings should be null terminated but there can be situations where a null character may not be the last character. In this case, it continues to copy garbage values as well.

  • strcat(): The reason behind this is same as that of strcat() and leads to garbage values.

  • sprintf(): It is a level above printf and stores the text to be printed as a string in a buffer. The buffer should be long enough to store the contents or it may result in overflow which results in unexpected behaviour.

  • vsprintf(): It is similar to sprintf() except the fact that it creates the buffer contents completely from variables. The issue remains the same that the memory should be sufficient for correctness.

Other functions like strncpy() and sprintf() are relatively safer versions considering that it requires the programmer to set limits. The problem that limits can be set wrongly is a real case. To tackle this, some projects consider these functions to be banned as well.

The solution is to natively implement required functionality. To enforce this and ensure nothing creepes into the project, these guidelines of banning a function are followed.

This is a standard approach provided it is used in large corporations and open source projects like:

  • Git
  • TensorFlow

How to ban a function?

To ban a set of functions, we need to follow the following steps:

  • define a header file name banned.h containing all macro definitions and functions that needs to be banned. (structure given later)
  • Include the above header file in each source code after including the other header files.
  • Compilation will fail if the banned functions are used.

The key idea is to:

  • Undefine the macro of the function name to ensure that previous definitions do not work. For this the header file needs to be included last.
  • define a macro named BANNED
  • define macros for all banned functions pointing to BANNED macro.

Points to be noted:

  • All macros needs to be defined in header files only
  • The banned header file should be included last

This will ensure that the approach works correctly and the programmer is not able to bypass it.

Contents of the banned.h header file banning the function strcpy:

#ifndef BANNED_H
#define BANNED_H

#define BANNED(func) sorry_##func##_is_a_banned_function

#undef strcpy
#define strcpy(x,y) BANNED(strcpy)

#endif /* BANNED_H */

If we want to ban another function say sprintf(), we need to add the following lines in the above header file code:

#undef sprintf
#define sprintf(x,y) BANNED(strcpy)

Now, we need to include banned.h in each of our source code file.

Consider this example use of strcpy:

#include<stdio.h> 
#include<string.h> 
#include"banned.h" 

int main () 
{ 
    char str1[]="opengenus"; 
    char str2[] = "some data"; 
    
    strcpy(str2, str1); 
    printf ("str1: %s\nstr2: %s\n", str1, str2); 
    return 0; 
} 

Compilation Error:

In file included from bb1.cpp:4:0:
bb1.cpp: In function ‘int main()’:
bb.h:12:22: error: ‘sorry_strcpy_is_a_banned_function’ was not declared in this scope
 #define BANNED(func) sorry_##func##_is_a_banned_function
                      ^
bb.h:15:21: note: in expansion of macro ‘BANNED’
 #define strcpy(x,y) BANNED(strcpy)
                     ^
bb1.cpp:11:5: note: in expansion of macro ‘strcpy’
     strcpy(str2, str1);
     ^

Note that we have not banned strncpy() (a variant and safer version of strcpy) so it can be used. Consider this example to understand it:

#include<stdio.h> 
#include<string.h> 
#include"banned.h" 

int main () 
{ 
    char str1[]="opengenus"; 
    char str2[] = "some data"; 
    
    // strcpy(str2, str1); 
    strncpy(str2, str1, 5);
    printf ("str1: %s\nstr2: %s\n", str1, str2); 
    return 0; 
} 

Output:

str1: opengenus
str2: opengdata

This has been published at OpenGenus IQ (here)

Real life example (in Git)

Git uses this approach to ban the following C functions:

  • strcpy
  • strcat
  • strncpy
  • strncat
  • sprintf
  • vsprintf

Banned functions in Git:

#ifndef BANNED_H
#define BANNED_H

/*
 * This header lists functions that have been banned from our code base,
 * because they're too easy to misuse (and even if used correctly,
 * complicate audits). Including this header turns them into compile-time
 * errors.
 */

#define BANNED(func) sorry_##func##_is_a_banned_function

#undef strcpy
#define strcpy(x,y) BANNED(strcpy)
#undef strcat
#define strcat(x,y) BANNED(strcat)
#undef strncpy
#define strncpy(x,y,n) BANNED(strncpy)
#undef strncat
#define strncat(x,y,n) BANNED(strncat)

#undef sprintf
#undef vsprintf
#ifdef HAVE_VARIADIC_MACROS
#define sprintf(...) BANNED(sprintf)
#define vsprintf(...) BANNED(vsprintf)
#else
#define sprintf(buf,fmt,arg) BANNED(sprintf)
#define vsprintf(buf,fmt,arg) BANNED(vsprintf)
#endif

#endif /* BANNED_H */

This is imported in git-compat-util.h which is imported in all git utilities/ source code files.

Use this technique in this C/ C++ projects and work like a professional C Programmer. Enjoy.