| PRE32 |
Do not use preprocessor directives in invocations of function-like macros |
Does not map to Rust |
Why not?This rule is needed in C due to how its preprocessor works. It's very C-preprocessor-specific, and doesn't map to Rust |
feedback |
| DCL31 |
Declare identifiers before using them |
Does not map to Rust |
Why not?Inferred types in Rust must always type-check. Not doing so results in a compile-time error. Therefore, the use case for this rule doesn't exist in Rust. |
feedback |
| DCL37 |
Do not declare or define a reserved identifier
Click for contextIn C, reserved identifiers include more than just keywords. They include library functions and variables (eg memcpy, errno)), including some well-known ones (eg abs). C also lists many identifiers as potentially reserved (eg anything beginning with str). It is possible to redefine (and thus override) such global identifiers in C, such as by adding #define errno my_errno to a header, which would break most C programs. |
Does not map to Rust |
Why not? Rust's scoping rules already prevent this kind of breakage. Any form of behavior override is locally scoped, and explicitly opted into by the affected parts. |
1, 2, 3 |
| EXP37 |
Call functions with the correct number and type of arguments |
Does not map to Rust |
Why not?This is enforced by the type system |
feedback |
| EXP44 |
Do not rely on side effects in operands to sizeof, _Alignof or _Generic |
Does not map to Rust |
Why not? This is C-specific |
feedback |
| EXP47 |
Do not call va_arg with an argument of the incorrect type |
Does not map to Rust |
Why not?Variadic Functions don't exist in Safe Rust. They do exist in Unsafe Rust, in the form of C's Variadic Functions. These exist as a way to declare and implement variadic functions for FFI contexts.
That being said, this rule doesn't map to Rust, because while it is possible to implement C variadic functions incorrectly, that issue is covered by the more general rule of Callers of any unsafe fn shall abide by the function's Safety contract.
Addendum: it is very reasonable to argue for a rule along the lines of "C-variadic functions should not be used outside of FFI contexts", but such a rule would not be a mapping of CERT-C's EXP47 rule. That is left for future work. |
1, 2, 3 |
| STR34 |
Cast characters to unsigned char before converting to larger integer sizes |
Does not map to Rust |
Why not?Characters in Rust are not integers. They are unicode scalar values. |
feedback |
| FIO30 |
Exclude user input from format strings |
Does not map to Rust |
Why not?Format strings are evaluated at compile time in Rust |
feedback |
| FIO41 |
Do not call getc(), putc(), getwc() or putwc() with a stream argument that has side effects |
Does not map to Rust |
Why not?This rule arises from how those APIs are implemented (as C macros), and not from a more general aspect of the situation. |
feedback |
| ENV30 |
Do not modify the object referenced by the return value of certain functions |
Does not map to Rust |
Why not?Immutability, when required, is always enforced in Rust.
I believe that Unsafe Rust could get away with doing this, jumping through some hoops. But that would lie in the vecinity of "Do not modify values whose validity invariant requires them to be immutable". Breaking validity invariants is, of course, UB. |
feedback |
| ENV32 |
All exit handlers must return normally |
Does not map to Rust |
Why not?Panicking doesn't have UB. Panics inside of panics will eventually abort the program. |
feedback |
| ERR34 |
Detect errors when converting a string to a number |
Does not map to Rust |
Why not?Functions in std that may fail, wrap their result in the Result<T, E> type, which forces the caller to handle the error case whenever it happens. Thus, this rule is already enforced by the compiler. |
feedback |
| INT31 |
Ensure that integer conversions do not result in lost or misinterpreted data |
Definitely maps to Rust |
Already being worked in #185; still review mapping/category |
feedback |
| FLP32 |
Prevent or detect domain and range errors in math functions |
Definitely maps to Rust |
|
feedback |
| ENV33 |
Do not call system() |
Definitely maps to Rust |
How would it map?The mapping is not exact (it doesn't involve system()):
If running commands, their input must always be sanitized, and their behavior should be made portable.
There is a Clippy lint that touches on this topic. We probably want a set of rules to address how system commands may be called. |
1, 2, 3 |
| MSC41 |
Never hard code sensitive information |
Definitely maps to Rust |
|
feedback |
| MEM31 |
Free dynamically allocated memory when no longer needed |
Maybe |
RationaleThe Rust compiler mostly enforces this automatically.
Important exceptions in Safe Rust are std::rc::Rc/std::sync::Arc cycles, which the user should avoid using the corresponding std::rc::Weak and std::sync::Weak.
Memory leaks in Rust are relatively easy to avoid (compared to manual malloc and free in C), but they are nonetheless still possible. Furthermore, memory leaks are considered safe in Rust.
Much like the original rule, our mapping would describe a defect of low severity, as their worst possible consequence is a Denial of Service due to memory exhaustion. |
feedback |
| DCL30 |
Declare objects with appropriate storage durations |
Maps directly to Unsafe Rust |
How would it map? In Safe Rust, the borrow checker enforces all accesses to memory to be within the lifetime of said memory.
In Unsafe, the borrow checker is not disabled. However, it is possible to use unsafe features to extend the lifetime of references (e.g. transmute or raw pointers). This is not always UB; the user must document and justify their decision to extend a lifetime, explaining why it's correct to do so.
That being said, almost all of Unsafe can be used to perform a use-after-free. So it might be the case that this rule spawns multiple others.
Zulip thread: Quick questions about UB |
feedback |
| EXP42 |
Do not compare padding data |
Maps directly to Unsafe Rust |
How would it map? Byte-wise comparisons are impossible in Safe Rust.
You can do so in Unsafe Rust, but this is discouraged nonetheless: there's no guarantee as to what the value of padding bits will be. Just don't do it!
UCG: Padding may be a good supplementary source for this. |
feedback |
| ENV31 |
Do not rely on an environment pointer following an operation that may invalidate it |
Maps directly to Unsafe Rust |
Recently moved into the unsafe bucket
RationaleThis interaction maps to Rust through the std::env::set_var and std::env::remove_var APIs.
In Unix, they are only safe to use in single-threaded programs. Such is the reality of those system APIs at the moment of writing (28-Feb-2026).
The rule would forbid both APIs in multithreaded code running on Unix systems. |
feedback |
Context
Thanks for taking a look at this batch.
This issue is part of the follow-on work from #336, where Félix finished a first pass mapping CERT C rules into Rust-oriented categories.
The goal here is to get a focused review on one slice of the 103 rules without making anyone wade through the whole set at once.
If you notice something that cuts across batches, or you want to leave a more global comment, please put that on #336.
If you want to read the original CERT wording before reviewing a mapping, the rule IDs in the table below link directly to the CMU SEI Confluence page for each rule. You may also find the top-level SEI CERT C Coding Standard useful for broader context.
Reviewer note
Please feel free to leave working notes here as you go.
When you feel done with this batch, please leave a short final summary on #336 and link back to this issue so the parent thread stays the main record of the outcome.
Review goal
For each rule below, please check whether the current proposed bucket still feels right for Rust.
If you disagree, are unsure, or think the rule should be merged with another concern, that is useful feedback too. A short rationale is enough.
Current buckets:
Rules in this batch
Please do not feel boxed in by the current buckets. If one of these looks miscategorized, that is exactly the kind of feedback we want.
Why not?
This rule is needed in C due to how its preprocessor works. It's very C-preprocessor-specific, and doesn't map to RustWhy not?
Inferred types in Rust must always type-check. Not doing so results in a compile-time error. Therefore, the use case for this rule doesn't exist in Rust.Click for context
In C, reserved identifiers include more than just keywords. They include library functions and variables (egmemcpy,errno)), including some well-known ones (egabs). C also lists many identifiers as potentially reserved (eg anything beginning withstr).It is possible to redefine (and thus override) such global identifiers in C, such as by adding
#define errno my_errnoto a header, which would break most C programs.Why not?
Rust's scoping rules already prevent this kind of breakage. Any form of behavior override is locally scoped, and explicitly opted into by the affected parts.Why not?
This is enforced by the type systemWhy not?
This is C-specificWhy not?
Variadic Functions don't exist in Safe Rust. They do exist in Unsafe Rust, in the form of C's Variadic Functions. These exist as a way to declare and implement variadic functions for FFI contexts.That being said, this rule doesn't map to Rust, because while it is possible to implement C variadic functions incorrectly, that issue is covered by the more general rule of Callers of any unsafe fn shall abide by the function's Safety contract.
Addendum: it is very reasonable to argue for a rule along the lines of "C-variadic functions should not be used outside of FFI contexts", but such a rule would not be a mapping of CERT-C's EXP47 rule. That is left for future work.
Why not?
Characters in Rust are not integers.They are unicode scalar values.
Why not?
Format strings are evaluated at compile time in RustWhy not?
This rule arises from how those APIs are implemented (as C macros), and not from a more general aspect of the situation.Why not?
Immutability, when required, is always enforced in Rust.I believe that Unsafe Rust could get away with doing this, jumping through some hoops. But that would lie in the vecinity of "Do not modify values whose validity invariant requires them to be immutable". Breaking validity invariants is, of course, UB.
Why not?
Panicking doesn't have UB. Panics inside of panics will eventually abort the program.Why not?
Functions in std that may fail, wrap their result in theResult<T, E>type, which forces the caller to handle the error case whenever it happens. Thus, this rule is already enforced by the compiler.How would it map?
The mapping is not exact (it doesn't involve system()):If running commands, their input must always be sanitized, and their behavior should be made portable.
There is a Clippy lint that touches on this topic. We probably want a set of rules to address how system commands may be called.
Rationale
The Rust compiler mostly enforces this automatically.Important exceptions in Safe Rust are
std::rc::Rc/std::sync::Arccycles, which the user should avoid using the correspondingstd::rc::Weakandstd::sync::Weak.Memory leaks in Rust are relatively easy to avoid (compared to manual
mallocandfreein C), but they are nonetheless still possible. Furthermore, memory leaks are considered safe in Rust.Much like the original rule, our mapping would describe a defect of low severity, as their worst possible consequence is a Denial of Service due to memory exhaustion.
How would it map?
In Safe Rust, the borrow checker enforces all accesses to memory to be within the lifetime of said memory.In Unsafe, the borrow checker is not disabled. However, it is possible to use
unsafefeatures to extend the lifetime of references (e.g.transmuteor raw pointers). This is not always UB; the user must document and justify their decision to extend a lifetime, explaining why it's correct to do so.That being said, almost all of Unsafe can be used to perform a use-after-free. So it might be the case that this rule spawns multiple others.
Zulip thread: Quick questions about UB
How would it map?
Byte-wise comparisons are impossible in Safe Rust.You can do so in Unsafe Rust, but this is discouraged nonetheless: there's no guarantee as to what the value of padding bits will be. Just don't do it!
UCG: Padding may be a good supplementary source for this.
Rationale
This interaction maps to Rust through thestd::env::set_varandstd::env::remove_varAPIs.In Unix, they are only safe to use in single-threaded programs. Such is the reality of those system APIs at the moment of writing (28-Feb-2026).
The rule would forbid both APIs in multithreaded code running on Unix systems.
What would help
After opening this issue
Post this comment to request a reviewer from the Producer queue: