Skip to content
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

layering_check doesn't work without sandboxing #21592

Closed
fmeum opened this issue Mar 6, 2024 · 9 comments
Closed

layering_check doesn't work without sandboxing #21592

fmeum opened this issue Mar 6, 2024 · 9 comments
Labels
P4 This is either out of scope or we don't have bandwidth to review a PR. (No assignee) team-Rules-CPP Issues for C++ rules type: bug

Comments

@fmeum
Copy link
Collaborator

fmeum commented Mar 6, 2024

Description of the bug:

When compiling C++ targets with the layering_check feature, Bazel only stages the clang module maps of direct dependencies of a target as inputs, not all transitive module maps:

builder.addAll(directModuleMaps);
builder.addTransitive(nonCodeInputs);
if (cppModuleMap != null && propagateModuleMapAsActionInput) {
builder.add(cppModuleMap.getArtifact());
}

As clang doesn't seem to fail if it can't find a module map file referenced by another module map, this works fine as long as sandboxing is enabled. However, if the compilation action doesn't run sandboxed, clang may load the referenced transitive module maps files, resulting in a "missing dependency declaration" error.

Which category does this issue belong to?

C++ Rules

What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

# .bazelrc
build --features=layering_check
build --repo_env=CC=clang

# MODULE.bazel
bazel_dep(name = "abseil-cpp", version = "20240116.1")
$ bazel clean --expunge && bazel --nohome_rc --nosystem_rc build @abseil-cpp//absl/base:throw_delegate --spawn_strategy=sandboxed
INFO: Invocation ID: aa6bf76b-d655-4601-904a-654a621f86ca
INFO: Starting clean (this may take a while). Consider using --async if the clean takes more than several minutes.
Starting local Bazel server and connecting to it...
INFO: Analyzed target @@abseil-cpp~20240116.1//absl/base:throw_delegate (70 packages loaded, 345 targets configured).
INFO: Found 1 target...
Target @@abseil-cpp~20240116.1//absl/base:throw_delegate up-to-date:
  bazel-bin/external/abseil-cpp~20240116.1/absl/base/libthrow_delegate.a
  bazel-bin/external/abseil-cpp~20240116.1/absl/base/libthrow_delegate.so
INFO: Elapsed time: 8.820s, Critical Path: 0.70s
INFO: 15 processes: 10 internal, 5 linux-sandbox.
INFO: Build completed successfully, 15 total actions

$ bazel clean --expunge && bazel --nohome_rc --nosystem_rc build @abseil-cpp//absl/base:throw_delegate --spawn_strategy=standalone
INFO: Invocation ID: 550feb94-c0c0-4f84-a50d-b17a2d73dbe3
INFO: Starting clean (this may take a while). Consider using --async if the clean takes more than several minutes.
Starting local Bazel server and connecting to it...
INFO: Analyzed target @@abseil-cpp~20240116.1//absl/base:throw_delegate (70 packages loaded, 345 targets configured).
ERROR: /home/fhenneke/.cache/bazel/_bazel_fhenneke/1265e716d099d259b9c08d61b24f01b4/external/abseil-cpp~20240116.1/absl/base/BUILD.bazel:339:11: Compiling absl/base/internal/throw_delegate.cc failed: undeclared inclusion(s) in rule '@@abseil-cpp~20240116.1//absl/base:throw_delegate':
this rule is missing dependency declarations for the following files included by 'absl/base/internal/throw_delegate.cc':
  'bazel-out/k8-fastbuild/bin/external/abseil-cpp~20240116.1/absl/base/core_headers.cppmap'
  'bazel-out/k8-fastbuild/bin/external/abseil-cpp~20240116.1/absl/base/atomic_hook.cppmap'
  'bazel-out/k8-fastbuild/bin/external/abseil-cpp~20240116.1/absl/base/errno_saver.cppmap'
  'bazel-out/k8-fastbuild/bin/external/abseil-cpp~20240116.1/absl/base/log_severity.cppmap'
Target @@abseil-cpp~20240116.1//absl/base:throw_delegate failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 8.400s, Critical Path: 0.59s
INFO: 13 processes: 11 internal, 2 local.
ERROR: Build did NOT complete successfully

Which operating system are you running Bazel on?

Linux

What is the output of bazel info release?

7.0.2

If bazel info release returns development version or (@non-git), tell us how you built Bazel.

No response

What's the output of git remote get-url origin; git rev-parse HEAD ?

No response

Is this a regression? If yes, please try to identify the Bazel commit where the bug was introduced.

No response

Have you found anything relevant by searching the web?

No response

Any other information, logs, or outputs that you want to share?

This was originally discovered in bazelbuild/rules_fuzzing#242 (comment).

@fmeum
Copy link
Collaborator Author

fmeum commented Mar 6, 2024

I briefly discussed this with @oquenchil, who mentioned that this was likely a design decision for Blaze. This leads to a few questions:

  1. How does include scanning interact with the need to stage (or not to stage) transitive module maps?
  2. Is it guaranteed that clang won't fail when it can't find a module map referenced by another module map? This behavior was pretty surprising to me.
  3. Since LLVM 17, clang can suggest a module providing a header included from a non-direct dependency (llvm/llvm-project@ab0116e). This convenience feature is similar to JavaBuilder strict deps fixups, but requires transitive module maps. Does Google have an alternative for this feature that doesn't require more inputs?

@fmeum fmeum changed the title layering_check requires sandboxing layering_check doesn't work without sandboxing Mar 6, 2024
DavidKorczynski pushed a commit to google/oss-fuzz that referenced this issue Mar 7, 2024
@comius comius added P4 This is either out of scope or we don't have bandwidth to review a PR. (No assignee) and removed untriaged labels Mar 21, 2024
@keith
Copy link
Member

keith commented Mar 22, 2024

Hit this same case trying to upgrade LLVM to bazel 7.x, not sure yet how I can workaround it

@fmeum
Copy link
Collaborator Author

fmeum commented Mar 22, 2024

@keith Does it happen with sandboxing enabled?

@keith
Copy link
Member

keith commented Mar 22, 2024

Yes, but I can't currently repro to test without sandboxing llvm/llvm-project#86297 (comment)

@redsun82
Copy link

We stumbled upon this issue here. We need to turn off sandboxing to do static analysis. Our workaround just consists in disabling the feature, which seems to be on by default:

bazel build //your/target --spawn_strategy=local --features=-layering_check

@keith
Copy link
Member

keith commented Mar 27, 2024

Debugging this a bit, it appears the issue is that when sandboxing is disabled the target's .d file ends up with dependencies on more cppmaps than it depends on (presumably from its transitives) which leads to this error, which is actually correct if those were really being depended on. Looking for a fix

@keith
Copy link
Member

keith commented Mar 27, 2024

This is trivially reproducible outside of a bazel as well. It appears clang includes any transitive modulemaps in the dotd file when they are readable (hence the sandboxing requirement here), even if there is no direct dependency on them.

@keith
Copy link
Member

keith commented Mar 27, 2024

submitted a fix here #21832, please leave any feedback there!

@keith
Copy link
Member

keith commented Apr 30, 2024

I noticed that this case is actually super hard to get out of as well I think. Unsurprising since it's theoretically non-hermetic but I think you have to clean expunge in some cases.

keith added a commit that referenced this issue May 14, 2024
bazel-io pushed a commit to bazel-io/bazel that referenced this issue Jul 26, 2024
When clang generates dotd files when using `-fmodule-map-file` any `extern module` directives in the modulemap are included in the dotd file if they exist. The result of this was that with sandboxing disabled the dotd file included transitive cppmap files that weren't in its input set, resulting in build failures. This change excludes those instead since they're not required as evidence by the fact that with sandboxing enabled they are not part of the input set.

Fixes bazelbuild#21592

Closes bazelbuild#21832.

PiperOrigin-RevId: 656382428
Change-Id: I4bc9802884ce1bc66ceda65a602db8dffbd1d9ea
github-merge-queue bot pushed a commit that referenced this issue Jul 26, 2024
When clang generates dotd files when using `-fmodule-map-file` any
`extern module` directives in the modulemap are included in the dotd
file if they exist. The result of this was that with sandboxing disabled
the dotd file included transitive cppmap files that weren't in its input
set, resulting in build failures. This change excludes those instead
since they're not required as evidence by the fact that with sandboxing
enabled they are not part of the input set.

Fixes #21592

Closes #21832.

PiperOrigin-RevId: 656382428
Change-Id: I4bc9802884ce1bc66ceda65a602db8dffbd1d9ea

Commit
ad53147

Co-authored-by: Keith Smiley <keithbsmiley@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P4 This is either out of scope or we don't have bandwidth to review a PR. (No assignee) team-Rules-CPP Issues for C++ rules type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants