Have you ever thought about your #include directives if everything is compiling fine? Sometimes you include header files you don’t need. Often you miss some, and your code works because other header files included the ones you need.
Wouldn’t it be nice if there was an easy way to tell if you got your #includes right? In this post I will show you how I explored Include What You Use on a new test case for the Linux Test Project (LTP)
Include What You Use
include-what-you-use builds on clang to analyze C and C++ files for their #include-directives.
At least for my distribution (currently openSUSE Leap 42.3) no packages are available, so I followed the installation instructions for my clang version, which is 3.8. Be sure to read and follow the instructions in README.md. I got it up and running in no time, but instead of copying the clang header files over to the install location, I just symlinked them.
Using include-what-you-use on a LTP test case
Currently I create a bunch of regression tests for already fixed CVE’s in the Linux kernel and in order to submit my patches for review, I want them to be as clean as possible.
If you want to change your compiler for building LTP, you would normally do the following (see the documentation):
make -k CC=include-what-you-use
However, I strongly suggest building the entire LTP with a normal C compiler. The code base is huge, and you would only want to check one file at a time, don’t you? Oh and, you can use clang to build LTP.
$ make or $ make CC=clang
Now I saved my new test case as
testcases/cve/cve-2017-16939.c and finally analyzed it:
$ make -k CC=include-what-you-use include-what-you-use -g -O2 -g -O2 -fno-strict-aliasing -pipe -Wall -W -Wold-style-definition -D_GNU_SOURCE -D_FORTIFY_SOURCE=2 -I../../include -I../../include -I../../include/old/ -L../../lib cve-2017-16939.c -lltp -o cve-2017-16939 warning: -lltp: 'linker' input unused warning: argument unused during compilation: '-L../../lib' cve-2017-16939.c should add these lines: #include <unistd.h> // for usleep, pid_t cve-2017-16939.c should remove these lines: - #include <netinet/in.h> // lines 31-31 - #include <sys/wait.h> // lines 30-30 The full include-list for cve-2017-16939.c: #include <linux/netlink.h> // for nlmsghdr, sockaddr_nl, NETLINK_XFRM #include <linux/xfrm.h> // for XFRMNLGRP_NONE, XFRM_MSG_GETPOLICY #include <sched.h> // for unshare, CLONE_NEWNET, CLONE_NEWUSER #include <stdlib.h> // for exit, WIFEXITED #include <string.h> // for memset #include <sys/socket.h> // for socket, AF_NETLINK, PF_NETLINK, SOCK_RAW #include "tst_res_flags.h" // for TCONF, TFAIL, TPASS #include "tst_safe_macros.h" // for SAFE_MALLOC, SAFE_WAITPID #include "tst_safe_net.h" // for SAFE_SENDTO, SAFE_SETSOCKOPT #include "tst_test.h" // for tst_brk, tst_res, SAFE_FORK, tst_test ---
Looking at the results
include-what-you-use tells me to add an include to
unistd.h. That is totally right, and the build did not fail by accident. So this is to be added.
sys/wait.h was included by accident - I had a call to
waitpid() in the code. However, LTP has a
SAFE_WAITPID() macro, and I replaced the call, but I forgot to remove the include.
If I would remove
netinet/in.h, the build would fail. This is a mistake of
include-what-you-use. If you use the
fix_includes.py-script, you would want to add
// IWYU pragma: keep at the end
of this line. For my case, I just ignored this one.
The other includes were found to be correct, and the output even shows the symbols that a header file is needed for.
Why did I do this? Well, primarily because I stumbled upon
include-what-you-use. I was curious, and I played with it.
However, it is at least good practice not to include header files you don’t need, as they could, at least theoretically, interfere with your software in a way you don’t want to.
On the other hand, relying on implicitly included headers may be a portability issue.
I think I will use this tool more regularly now.