Unintended public exposure of C symbols via @cImport in Zig
0 reputation · 07 Aug 2021, 22:11 UTC
0 reputation · 07 Aug 2021, 22:11 UTC
Goal: Verify whether placing a @cImport block inside a pub block causes the imported C declarations to become part of the module’s public API, potentially allowing other Zig files to access them without an explicit pub qualifier.
Constraints: Zig defaults to file‑private visibility; a @cImport inherits the visibility of its surrounding block. The compiler warns in version 0.13.0 when the import is inside a pub block, but it is unclear if this warning fully mitigates the risk or if additional patterns are needed to keep C symbols private while still usable internally.
Questions:
35775 reputation · 08 Aug 2021, 04:19 UTC
@cImport at file scope (or inside any non-pub block) keeps all imported C declarations file‑private. They cannot be accessed from other files unless you explicitly re‑export them.const at file scope, then selectively re‑export only what you need. Example:
// file.zig
const c = @cImport({
@cInclude("stdlib.h");
});
pub const malloc = c.malloc;
// free, calloc, etc. stay private
@cImport appears directly in a pub block. It does not catch transitive leaks through pub usingnamespace or re‑exports from dependencies. Treat it as a backstop, not a substitute for deliberate API design.Zig defaults to file‑private visibility. Any declaration at file scope — including a const that holds an @cImport result — is invisible outside that file unless marked pub. The @cImport expression itself has no visibility; it inherits the visibility of the block it sits in. Therefore:
pub { @cImport(...) } → every C declaration becomes public.const c = @cImport(...); (no pub) → the whole c namespace is private; only pub const malloc = c.malloc; (or similar) escapes.a.zig:
const c = @cImport({ @cInclude("stdlib.h"); });
b.zig:
const A = @import("a.zig");
const _ = A.c.malloc; // should fail: private field
zig build-exe b.zig — expect a “private field” error.a.zig to pub { @cImport({ @cInclude("stdlib.h"); }) } and rebuild — compilation now succeeds, confirming the leak.-Werror to your build to promote the pub-block warning to a hard error in CI.pub @cImport internally, those C symbols become part of its public API and therefore reachable by you. Audit build.zig.zon dependencies if you see unexpected C symbols.pub usingnamespace @cImport(...): This is the most leak‑prone pattern — avoid it in public headers.Are you building a library (intended for zig build --prefix consumption) or an executable? For libraries, the private-const pattern is mandatory to avoid polluting downstream consumers. For executables, the risk is lower but the same pattern keeps internal boundaries clean.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.