Skip to content

Implicit inclusion of checked header files. #998

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

Merged
merged 12 commits into from
Apr 1, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 54 additions & 59 deletions clang/docs/checkedc/rfc/Include-Checked-Hdrs.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,29 @@ programs to include the checked counterparts of system header files either
explicitly or implicitly during the conversion of these programs to Checked C.

## Problem Summary
The existing approach for including checked header files, which is to explicitly
The current approach for including checked header files, which is to explicitly
replace an included header file with its checked counterpart, is inconvenient
for automation by 3C. So we need a way to implicitly include checked header
for adoption to Checked C - all the system header files included in a project
need to be altered. So we need a way to implicitly include checked header
files.

## Conditions that a Solution must Satisfy
- The solution must not violate the "opt-in" philosophy that we advocate for
the conversion of a program to Checked C. In accordance with this, the
Checked C compiler must not silently perform an implicit inclusion of the
checked header files.
- The solution must be easy to integrate into a build system.
- The existing approach of explicitly including checked header files must
- The solution must provide users an option to either opt into the implicit
inclusion of checked header files, or opt out of it if checked header files
are implicitly included by default. This flexibility is required to support
custom build environments.
- The solution must be easy to integrate into an existing build system.
- The current approach of explicitly including checked header files must
always remain a viable alternative.
- It should be possible to directly include certain system header files and
bypass the inclusion of their checked counterparts in order to support custom
build environments.
- The order in which the compiler driver searches the include paths must remain
unaltered.
- The solution must be able to accommodate any clang-specific declarations in
system header files, if present (Ex.: inttypes.h).
- The Checked-C-specific declarations must live in checkedc repository.
- The Checked-C-specific declarations must live in the checkedc repository.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked-C-specific --> Checked C-specific

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your review! I feel that Checked-C-specific is a bit more clear - so I have retained the same.



## Details of the Existing Approach
- The existing approach supports only the explicit inclusion of checked header
## Details of the Current Approach
- The current approach supports only the explicit inclusion of checked header
files.
- An installed Checked C compiler places the checked counterparts of system
header files like `stdio_checked.h` in the directory
Expand All @@ -53,14 +51,14 @@ files.
clang picks it up from the above location.

## Solution Space
- Solution 1: Proposed by CCI
- Solution 1:
- Let `foo.h` be a system header file with a checked counterpart called
`foo_checked.h`. We add a new file, also called `foo.h`, in the same
directory that contains `foo_checked.h`. This new `foo.h` should contain
`#include_next <foo.h>` plus all the Checked-C-specific declarations
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked-C-specific --> Checked C-specific

present in `foo_checked.h`. In addition, the original contents of
`foo_checked.h` should be deleted and it should now only contain
`#include <foo.h>`.
`#include <foo.h>`.
- The way this solution will work:
- If a program includes `foo.h`, clang will first pick up the
"checked" version of `foo.h`. As this `foo.h` has a
Expand All @@ -70,31 +68,31 @@ files.
will also be picked up.
- If the program includes `foo_checked.h`, the line `#include <foo.h>`
in `foo_checked.h` will initiate the same process as above.
- **Pros**: 1) It is convenient for automation. 2) The existing
approach of explicit inclusion is supported. 3) No changes are required to
the build system.
- **Cons**: 1) It violates the "opt-in" philosophy. 2) It does not provide
fine-grained control to directly include a system header file and avoid
Checked-C-specific declarations. 3) While it is easy to accommodate
clang-specific declarations in system header files (Checked-C-specific
declarations can be added after the clang-specific declarations), this
solution will require some Checked-C-specific declarations to be part of
checkedc-clang repository.
- **Pros**: 1) The current approach of explicit inclusion is supported.
2) No changes are required to the build system. 3) No changes are
required to the order in which the include directories are searched.
4) It is convenient for automation.
- **Cons**: 1) The solution does not provide a way to opt out of including
checked headers. 2) While it is easy to accommodate clang-specific
declarations in system header files (Checked-C-specific declarations can
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked-C-specific --> Checked C-specific

be added after the clang-specific declarations), this solution will
require some Checked-C-specific declarations to be part of checkedc-clang
repository.

- Solution 2:
- Have a different compiler driver to provide the facility for implicit
inclusion of header files.
- **Pros**: 1) It satisfies the "opt-in" philosophy. 2) It is convenient
for automation. 3) It provides fine-grained control to directly include a
system header file. 4) The existing approach of explicit inclusion is
supported. 5) In most cases integration into a build system will be easy:
the `CC` variable can be redefined appropriately.
inclusion of header files.
- **Pros**: 1) The solution provides the option to opt into implicitly
including checked header files. 2) The existing approach of explicit
inclusion is supported. 3) In most cases integration into a build system
will be easy: the `CC` variable can be redefined appropriately. 4) It is
convenient for automation.
- **Cons**: 1) It is a very heavy-weight solution. 2) In some cases
integrating with a build system will be more involved when both the drivers
are needed to compile the sources. This may happen in a scenario where most
source files want implicit inclusion but a few source files want explicit
inclusion because they want to directly include some system header files in
order to avoid Checked-C-specific declarations.
integrating with a build system will be more involved when both the
drivers are needed to compile the sources. This may happen in a scenario
where most source files want implicit inclusion but a few source files
want explicit inclusion because they want to directly include some system
header files in order to avoid Checked-C-specific declarations.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked-C-specific --> Checked C-specific


- Solution 3:
- Let `foo.h` be a system header file with its checked counterpart called
Expand All @@ -116,18 +114,20 @@ files.
commandline. Therefore specifying this flag will cause the implicit
inclusion of the checked counterpart of `foo.h`, which will make it
convenient for automation. Not specifying the commandline flag will
not perform implicit inclusion, satisfying our "opt-in" philosophy.
Infinite recursion is avoided because of `#include_next`.
not perform implicit inclusion, providing a mechanism to opt out of
implicit inclusion. Infinite recursion is avoided because of
`#include_next`.
- If a program includes `foo_checked.h` current behavior will prevail.
The above flag has no effect on the explicit inclusion of checked
header files.

- **Pros**: 1) It satisfies the "opt-in" philosophy. 2) It is convenient
for automation. 3) It provides fine-grained control to directly include a
system header file. 4) The existing approach of explicit inclusion is
supported. 5) In most cases integration with the build system is easy:
- **Pros**: 1) The solution provides a way to opt into implicit inclusion
of checked header files. 2) The existing approach of explicit inclusion
is supported. 3) In most cases integration with the build system is easy:
the flag `-DIMPLICIT_INCLUDE_CHECKED_HDRS` is passed to the compilation
of all source files through the `CFLAGS` variable.
of all source files through the `CFLAGS` variable. 4) No changes are
required to the order in which include directories are searched. 5) It
is convenient for automation.

- **Cons**: 1) In some cases integration with the build system will be more
involved if the above flag needs to be passed to the compilation of most
Expand All @@ -139,21 +139,17 @@ files.
## Proposed Solution:

The proposed solution is as follows:
- It is a hybrid solution based on ideas in Solutions 3 and 1 that
handles the presence of both clang-specific and Checked-C-specific
declarations in header files. It also incorporates the feedback from CCI to
make implicit inclusion of checked header files as default.
- We primarily adopt Solution 3 for header files that do not have
clang-specific declarations, but have Checked-C-specific declarations (there
are 13 such header files at present). To implicitly include checked headers
by default, the compilation flag (as proposed in Solution 3) now gives a way
to **opt out** of implicit inclusion. Accordingly, it is renamed to
NO_IMPLICIT_INCLUDE_CHECKED_HDRS.
- We will implicitly include checked header files by default and provide the
compilation flag NO_IMPLICIT_INCLUDE_CHECKED_HDRS to opt out of the implicit
inclusion.
- For header files that do not have clang-specific declarations, but have
Checked-C-specific declarations (there are 13 such header files at present),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked-C-specific --> Checked C-specific

we will do the following:
- For each system header file, say `foo.h`, that does not have
clang-specific declarations but has Checked-C-specific declarations, we
will add a new file also called `foo.h` that will contain the following:

#ifdef NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#if !defined __checkedc || defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include_next <foo.h>
#else
#include <foo_checked.h>
Expand All @@ -174,14 +170,13 @@ The proposed solution is as follows:

At the end of this pre-existing `bar.h`, we will add the following:

#ifndef NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#if defined __checkedc && !defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include <bar_checked_internal.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the same idea as my previous comment, this could be changed to just:

Suggested change
#include <bar_checked_internal.h>
#include <bar_checked.h>

Then the format of bar.h would be exactly the same as in the case of no Clang-specific declarations, except for the presence of the declarations between the #include_next <bar.h> and the Checked-C-specific block at the bottom.

#endif

- All the Checked-C-specific declarations (that are currently present in
`bar_checked.h`) will be moved to `bar_checked_internal.h`. The file
`bar_checked.h` will be modified to contain just the following:

// Force the inclusion of Checked-C-specific declarations
#undef NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include <bar.h>
#include <bar.h>
#include <bar_checked_internal.h>
2 changes: 2 additions & 0 deletions clang/lib/Frontend/InitPreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI,
Builder.defineMacro("__STDC_VERSION__", "199901L");
else if (!LangOpts.GNUMode && LangOpts.Digraphs)
Builder.defineMacro("__STDC_VERSION__", "199409L");
if (LangOpts.CheckedC)
Builder.defineMacro("__checkedc", "202104L");
} else {
// -- __cplusplus
// [C++20] The integer literal 202002L.
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/Headers/inttypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#error MSVC does not have inttypes.h prior to Visual Studio 2013
#endif

#ifndef __cplusplus
#ifdef __checkedc
#pragma CHECKED_SCOPE push
#pragma CHECKED_SCOPE off
#endif
Expand Down Expand Up @@ -99,11 +99,12 @@
#define SCNxFAST32 "x"
#endif

#ifndef __cplusplus
#ifdef __checkedc
#pragma CHECKED_SCOPE pop
#endif

#ifndef NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#if defined __checkedc && !defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS
// Compiling for Checked C and implicit inclusion of checked headers is enabled
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Period needed at the end of comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

#include <inttypes_checked_internal.h>
#endif

Expand Down
53 changes: 53 additions & 0 deletions clang/test/CheckedC/headers/nostdinc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// The -nostdinc option suppresses the search for include files in standard
// system include directories like /usr/local/include, /usr/include,
// /usr/include/<TARGET_ARCH>, and also the <BUILD>/lib/clang/<VERSION>/include
// directory.
// Therefore, in the context of a Checked C compilation, none of the
// header files listed below will be found when -nostdinc is specified on the
// compilation command line.
//
// This test confirms the above behavior.
//
// RUN: %clang -target x86_64-unknown-unknown \
// RUN: -nostdinc -ffreestanding -fsyntax-only %s

#if defined(__has_include)

#if __has_include (<assert.h>) \
|| __has_include (<errno.h>) \
|| __has_include (<fenv.h>) \
|| __has_include (<inttypes.h>) \
|| __has_include (<math.h>) \
|| __has_include (<signal.h>) \
|| __has_include (<stdio.h>) \
|| __has_include (<stdlib.h>) \
|| __has_include (<string.h>) \
|| __has_include (<threads.h>) \
|| __has_include (<time.h>) \
|| __has_include (<checkedc_extensions.h>) \
|| __has_include (<unistd.h>) \
|| __has_include (<sys/socket.h>) \
|| __has_include (<arpa/inet.h>)
#error "expected to *not* be able to find standard C headers"
#endif


#if __has_include (<assert_checked.h>) \
|| __has_include (<errno_checked.h>) \
|| __has_include (<fenv_checked.h>) \
|| __has_include (<inttypes_checked.h>) \
|| __has_include (<math_checked.h>) \
|| __has_include (<signal_checked.h>) \
|| __has_include (<stdio_checked.h>) \
|| __has_include (<stdlib_checked.h>) \
|| __has_include (<string_checked.h>) \
|| __has_include (<threads_checked.h>) \
|| __has_include (<time_checked.h>) \
|| __has_include (<checkedc_extensions.h>) \
|| __has_include (<unistd_checked.h>) \
|| __has_include (<sys/socket_checked.h>) \
|| __has_include (<arpa/inet_checked.h>)
#error "expected to *not* be able to find Checked C headers"
#endif

#endif
78 changes: 78 additions & 0 deletions clang/test/CheckedC/headers/nostdlibinc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// The -nostdlibinc option suppresses the search for include files in standard
// system include directories like /usr/local/include, /usr/include,
// /usr/include/<TARGET_ARCH>. But the <BUILD>/lib/clang/<VERSION>/include
// directory is still searched for include files.
// Therefore, in the context of a Checked C compilation, all the wrapper
// header files listed below are found while the corresponding system header
// file that is included in each of these files (via #include_next) is not
// found when -nostdlibinc is specified on the compilation command line.
//
// This test confirms the above behavior with the following checks:
// 1) The wrapper header files listed below are found.
// 2) The system header file of the same name that each wrapper header file
// includes (via #include_next), is not found.
// The -MM -MG preprocessor options list the header files not found.
//
// RUN: %clang -target x86_64-unknown-unknown -nostdlibinc -ffreestanding -MM -MG %s | FileCheck -check-prefix=CHECK %s
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default, FileCheck will always check the prefix CHECK. So you can omit the --check-prefix=CHECK part in this RUN command.
See https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-filecheck-check-prefix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.



#if defined(__has_include)

#if __has_include (<assert.h>)
#include <assert.h>
#endif
// CHECK: assert.h
#if __has_include (<errno.h>)
#include <errno.h>
#endif
// CHECK: errno.h
#if __has_include (<fenv.h>)
#include <fenv.h>
#endif
// CHECK: fenv.h
#if __has_include (<inttypes.h>)
#include <inttypes.h>
#endif
// CHECK: inttypes.h
#if __has_include (<math.h>)
#include <math.h>
#endif
// CHECK: math.h
#if __has_include (<signal.h>)
#include <signal.h>
#endif
// CHECK: signal.h
#if __has_include (<stdio.h>)
#include <stdio.h>
#endif
// CHECK: stdio.h
#if __has_include (<stdlib.h>)
#include <stdlib.h>
#endif
// CHECK: stdlib.h
#if __has_include (<string.h>)
#include <string.h>
#endif
// CHECK: string.h
#if __has_include (<threads.h>)
#include <threads.h>
#endif
// CHECK: threads.h
#if __has_include (<time.h>)
#include <time.h>
#endif
// CHECK: time.h
#if __has_include (<unistd.h>)
#include <unistd.h>
#endif
// CHECK: unistd.h
#if __has_include (<sys/socket.h>)
#include <sys/socket.h>
#endif
// CHECK: sys/socket.h
#if __has_include (<arpa/inet.h>)
#include <arpa/inet.h>
#endif
// CHECK: arpa/inet.h

#endif