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

Conversation

sulekhark
Copy link
Contributor

This PR has previous discussion in PR #980.

@sulekhark sulekhark requested a review from dtarditi March 2, 2021 18:50
@mattmccutchen-cci
Copy link
Member

I'm referencing checkedc/checkedc#431, which this addresses.

@@ -18,6 +18,11 @@
#error MSVC does not have inttypes.h prior to Visual Studio 2013
#endif

#ifndef __cplusplus
Copy link

Choose a reason for hiding this comment

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

Do we want this enabled even if the user compiles a C file with -fno-checkedc-extension?

Maybe we can create a macro like __checkedc which gets defined if Checked C is enabled. Then we can use that macro as guard here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we need to resolve this issue. We also need to consider the use case when inttypes_checked.h is explicitly included.

Copy link
Member

Choose a reason for hiding this comment

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

+1 to creating a macro like __checkedc. That would make it possible for the public headers of third-party libraries to automatically generate Checked C declarations when loaded by the Checked C compiler while remaining compatible with plain C compilers as well: in effect, supporting implicit inclusion of checked headers for third-party libraries, just as you are doing for the system headers in this PR. (I was thinking about filing a separate issue for this.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I have created the __checkedc macro in the latest commit.

@@ -1,10 +1,8 @@
// RUN: %clang -target x86_64-unknown-unknown \
// RUN: -nostdlibinc -ffreestanding -fsyntax-only %s
// 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.

Could you please explain why we need to change community tests?

Does this mean we are changing existing behavior, and that users would need to update their flags/makefiles? Changing community tests could also cause merge conflicts when we upgrade.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test case is ensuring that the system stdlib.h cannot be found if -nostdlibinc is specified on the compilation commandline.
This can be ensured in more than one way - the approach chosen in this test case assumes that stdlib.h is not present in <build-dir>/lib/clang/11.0.0/include directory. This is an assumption because of the following reason:
The mechanism used by clang/llvm to add clang-specific declarations is to create a header file of the same name (ex.: float.h, inttypes.h - these are present in <src-dir>/clang/lib/Headers), use #include_next <float.h> to include the system header file, and add clang-specific declarations below this. Then they copy the header file from <src-dir>/clang/lib/Headers to <build-dir>/lib/clang/11.0.0/include. The only reason that stdlib.h is not present in <build-dir/lib/clang/11.0.0/include in the llvm upstream repo is because there has not been a need to add clang-specific declarations to stdlib.h. Had there been, this test case would have to be modified anyway.

Now, that we have added stdlib.h to <build-dir>/lib/clang/11.0.0/include, we need to change the way of ensuring that the "system" stdlib.h is not found when -nostdlibinc is specified on the compilation command line.

Copy link

Choose a reason for hiding this comment

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

Once we create a new macro for Checked C and use it as guard maybe we can simply add -fno-checkedc-extension to these tests to preserve community behavior without necessarily modifying the tests themselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should create a new macro for Checked C and use it as a guard. But adding -fno-checkedc-extension to this test case will not stop it from failing. In it's original form, this test case will fail because of the fact that a file named stdlib.h exists in the <build-dir>/lib/clang/11.0.0/include directory, irrespective of what the file contains.

The above modification will be required to make this test case pass even if we create the new macro.

@sulekhark sulekhark changed the title Final version of the rfc for implicit inclusion of checked header files. Rfc for implicit inclusion of checked header files. Mar 11, 2021
Comment on lines +165 to +167
- For a system header file that contains clang-specific declarations and also
Checked-C-specific declarations (there is one such header file at present),
we will do the following:
Copy link
Member

Choose a reason for hiding this comment

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

If you're willing to change the default include path in the driver to add a Checked-C-specific include directory (say lib/clang/VERSION/include-checkedc) before the main one (lib/clang/VERSION/include), then you won't need to do this. You can use the solution in the previous bullet point for all Checked C headers, and the #include_next <foo.h> will automatically pick up either the system header or the Clang header (in which the #include_next <foo.h> would then include the system header) as appropriate. This would also cleanly separate the Checked C headers from the Clang headers in different subdirectories of the installation directory, making it clearer for users during troubleshooting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have decided not to change the default include path in the driver at present.

Comment on lines 185 to 187
// Force the inclusion of Checked-C-specific declarations
#undef NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include <bar.h>
Copy link
Member

Choose a reason for hiding this comment

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

If you do keep this approach rather than taking my suggestion above, might the following be safer?

Suggested change
// 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>

bar_checked_internal.h might get scanned twice, so it would need a header guard and there might be a slight performance cost, but it would make it possible for a user to set NO_IMPLICIT_INCLUDE_CHECKED_HDRS for one #include <foo.h> directive in a translation unit without the risk of an #include <bar_checked.h> earlier in the translation unit clobbering NO_IMPLICIT_INCLUDE_CHECKED_HDRS (if that is a scenario you care about).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mattmccutchen-cci Thank you for your suggestion - you are right! I have incorporated it in the latest commit.

@@ -18,6 +18,11 @@
#error MSVC does not have inttypes.h prior to Visual Studio 2013
#endif

#ifndef __cplusplus
Copy link
Member

Choose a reason for hiding this comment

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

+1 to creating a macro like __checkedc. That would make it possible for the public headers of third-party libraries to automatically generate Checked C declarations when loaded by the Checked C compiler while remaining compatible with plain C compilers as well: in effect, supporting implicit inclusion of checked headers for third-party libraries, just as you are doing for the system headers in this PR. (I was thinking about filing a separate issue for this.)

Copy link
Member

@dtarditi dtarditi left a comment

Choose a reason for hiding this comment

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

Thanks for putting this together - I have some suggestions for the RFC.

## Problem Summary
The existing 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
Copy link
Member

Choose a reason for hiding this comment

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

I would talk about this in more general terms. 3C is representative of an approach to converting files. It is inconvenient for adoption of Checked C to have to alter include files throughout a project.

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 have addressed all the review comments and made the requisite changes to the doc.

files.

## Conditions that a Solution must Satisfy
- The solution must not violate the "opt-in" philosophy that we advocate for
Copy link
Member

Choose a reason for hiding this comment

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

The proposed solution isn't opt-in, though.

clang picks it up from the above location.

## Solution Space
- Solution 1: Proposed by CCI
Copy link
Member

Choose a reason for hiding this comment

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

Probably better to give this a more general name, not "proposed by CCI".

## Proposed Solution:

The proposed solution is as follows:
- It is a hybrid solution based on ideas in Solutions 3 and 1 that
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't t his be classified as another possible solution? It is a little confusing for the proposed solution to be described as a hybrid of other solutions.


// Force the inclusion of Checked-C-specific declarations
#undef NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include <bar.h>
Copy link
Member

Choose a reason for hiding this comment

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

This approach of a slight alteration to the clang-specific header file looks good to me.

- 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.

@@ -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

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

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

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

#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.

// 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.

…cket.h

may not be found in all compilation environments (Ex. Windows).
Therefore the includes in the wrapper header files need to be
guarded by __has_include_next. This may impact the expected output of a
testcase.
Comment on lines +152 to +156
#if !defined __checkedc || defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include_next <foo.h>
#else
#include <foo_checked.h>
#endif
Copy link
Member

Choose a reason for hiding this comment

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

I realize this can be substantially simplified if you don't mind some header files being read multiple times. They probably are or have to be guarded anyway, so the only cost I see is performance, unless there is a concern about a header file indirectly including itself recursively even if the recursion is immediately stopped by a guard.

Suggested change
#if !defined __checkedc || defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include_next <foo.h>
#else
#include <foo_checked.h>
#endif
#include_next <foo.h>
#if defined __checkedc && !defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS
#include <foo_checked.h>
#endif

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I understand that this suggestion has the advantage of a uniform approach for all header files but it relies on guard flags to avoid recursion. In order to keep the solution simple and maintainable, we do prefer to avoid a header file including itself recursively without relying on guard flags.
Additionally, in the implementation, there are more flags entering into the fray in order to handle the absence of some posix header files like unistd.h, arpa/inet.h and sys/socket.h in the Windows environment. This makes it even more necessary to keep the solution simple and also comprehensible.

Copy link
Member

Choose a reason for hiding this comment

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

When I think about this now, I think I got a little carried away with my proposal and you were right to decline it. The resulting code looks simpler on the surface, but it's significantly more complicated to verify that it works correctly.

FWIW, an alternative way to achieve a uniform approach is to have each header foo.h with no Clang-specific declarations use your existing design for headers with Clang-specific declarations, effectively as if Clang already had a foo.h containing nothing but #include_next <foo.h> followed by an empty set of Clang-specific declarations. You could decide whether the resulting short, boilerplate foo.h goes in the checkedc-clang or checkedc repository; the former would achieve total uniformity. But this would increase the number of header files you have to maintain by about 50% (in addition to foo.h and foo_checked.h, you would have foo_checked_internal.h) for minimal practical benefit, so I imagine it's not worth it. (The only practical benefit I see is that if the user had some problem with the system foo.h but had another way to get the plain-C declarations they want, they could then get the Checked C declarations by including foo_checked_internal.h with no risk of pulling in the system foo.h. But I think this would be very unusual, and the user could use the hacky workaround of manually defining the header guard for foo.h before including foo_checked.h.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we did earlier consider having the uniform approach of creating *_checked_internal.h for all header files that have Checked-C-specific declarations. At present, there are 14 such header files of which inttypes.h is the only file with clang-specific declarations. We decided not to create *_checked_internal.h for all because 13 of these files will be redundant at present.
If a user wants custom plain-C declarations and also Checked C declarations for, say, foo.h, under the present system he/she can 1) include either foo.h (for implicit inclusion) or foo_checked.h (for explicit inclusion) and 2) add -nostdlibinc and the include directory containing custom plain-C declarations on the compilation command line.

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

#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.

Comment on lines +177 to +182
- 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:

#include <bar.h>
#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.

And then:

Suggested change
- 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:
#include <bar.h>
#include <bar_checked_internal.h>
- The file `bar_checked.h` will contain `#include <bar.h>` and the
Checked-C-specific declarations.

#include <foo_checked.h>
#endif

- The file `foo_checked.h` will contain `#include_next <foo.h>` and the
Copy link
Member

Choose a reason for hiding this comment

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

You could change the #include_next <foo.h> to #include <foo.h> (making it consistent with the case where Clang-specific declarations are present) if you add a guard to foo.h to avoid direct recursion between the two files. (Otherwise, foo.h does not need a guard because all it does is include two other files that presumably have their own guards. But if it's considered best practice for foo.h to have a guard anyway, then you could adopt this suggestion.)

are crashing, in order to test all the other test cases on the ADO build
machines.
@sulekhark
Copy link
Contributor Author

@mattmccutchen-cci Thanks for you review! I will think about your suggestions. Meanwhile, there are five 3C test cases that are crashing on Windows - they all include stdio.h and stdlib.h - so the root cause is most likely the same (3d-allocation.c, basic.c, graphs.c, graphs2.c, linkedlist.c). It will be great if you could take a look at them.

@mattmccutchen-cci
Copy link
Member

mattmccutchen-cci commented Mar 23, 2021

Meanwhile, there are five 3C test cases that are crashing on Windows - they all include stdio.h and stdlib.h - so the root cause is most likely the same (3d-allocation.c, basic.c, graphs.c, graphs2.c, linkedlist.c). It will be great if you could take a look at them.

I'll look into this. In the meantime, maybe you could work on #1009 since that's probably a blocker for turning on implicit checked header inclusion by default (it would break existing C code).

@mattmccutchen-cci
Copy link
Member

Re the 3C tests crashing on Windows: just looking at the state at the time of the crash, it seems we are using a Clang API incorrectly and this change fixes the tests for me. We need to investigate the problem further (correctcomputation#509), but you can go ahead and use this fix as a basis for further development of this PR and (if need be) merge it even if we haven't completed our investigation yet.

@sulekhark sulekhark requested a review from kkjeer March 30, 2021 20:21
Copy link
Member

@dtarditi dtarditi left a comment

Choose a reason for hiding this comment

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

Looks good, thank you! Please commit the changes in the other repos before this change, so automated testing does not fail.

@mattmccutchen-cci
Copy link
Member

@sulekhark I guess it's time for me to test this with the 3C benchmark test suite then? (I had been waiting for your confirmation.) I can do that later this morning.

@sulekhark
Copy link
Contributor Author

@mattmccutchen-cci Yes, please go ahead and test.

@sulekhark sulekhark merged commit 2023d30 into master Apr 1, 2021
@sulekhark sulekhark deleted the implicit-include-of-hdrfiles branch April 1, 2021 14:11
mattmccutchen-cci added a commit to correctcomputation/checkedc-clang that referenced this pull request Apr 1, 2021
default now that we've merged Microsoft's change for implicit checked
header inclusion (checkedc#998).

I filed #520
for follow-ups.
mattmccutchen-cci added a commit to correctcomputation/checkedc-clang that referenced this pull request Apr 1, 2021
default now that we've merged Microsoft's change for implicit checked
header inclusion (checkedc#998).

I filed #520
for follow-ups.

We don't have to change the benchmark workflow right now: it still
passes --includeDir, but that option is ignored when #include updating
is off.
@sulekhark sulekhark changed the title Rfc for implicit inclusion of checked header files. Implicit inclusion of checked header files. May 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants