How to fix code to avoid warning -Wunsafe-buffer-usage
Asked Answered
D

2

16

Before clang 16.0 I was able to compile all my projects using -Weveything without problems, fixing my code and paying attention to the (useful) warnings that this option gave. However, it seems that the -Wunsafe-buffer-usage was added in version 16 (my system updated to it recently) and then compiling any project is impossible without getting a huge list of useless warnings about any pointer being used, see minimal example below:

$ cat main.c
#include <stdio.h>

int main(int argc, char **argv) {
    if (argc != 2) {
        return 0;
    }
    printf("argv[1] = %s\n", argv[1]);
}

$ clang -Weverything main.c
main.c:3:27: warning: 'argv' is an unsafe pointer used for buffer access [-Wunsafe-buffer-usage]
int main(int argc, char **argv) {
                   ~~~~~~~^~~~
main.c:7:30: note: used in buffer access here
    printf("argv[1] = %s\n", argv[1]);
                             ^~~~
1 warning generated.

One can't possibly reason about his code using this warning.

The clang documentation has the following to say:

Since -Weverything enables every diagnostic, we generally don’t recommend using it. -Wall -Wextra are a better choice for most projects. Using -Weverything means that updating your compiler is more difficult because you’re exposed to experimental diagnostics which might be of lower quality than the default ones. If you do use -Weverything then we advise that you address all new compiler diagnostics as they get added to Clang, either by fixing everything they find or explicitly disabling that diagnostic with its corresponding Wno- option.

In this case, how would I "fix everything they find"?

Setting -Wno-unsafe-buffer-usage works, but it is an ugly solution. It adds clutter to a Makefile and feels like cheating.

Dill answered 31/8, 2023 at 16:4 Comment(20)
I'd offer that fighting with -Weverything may not be a battle you want to start. You may be able to make -Weverything happy in this case, but there will be many more warnings you'll need to satisfy, many of which may require making your code worse to "fix". For example, satisfying -Wdeclaration-after-statement would require declaring all local variables at the top of functions instead of as needed.Ligniform
I think the other warnings I have encountered so far are at least potentially useful. -Wdeclaration-after-statement makes me organize my declarations in the proper scopes. Note that it does not complain about for (int i = 0; i < n; i += 1). If it did, then I would agree with you with this particular example.Dill
Where I use -Weverything, I also counter some options with -Wno-xyz variants. One script I use has: -Weverything -Wno-padded -Wno-vla -Wno-reserved-id-macro -Wno-documentation-unknown-command -Wno-poison-system-directories -Wno-format-nonliteral. I build format strings in a number of the programs, hence -Wno-format-nonliteral. I use VLAs in matrix manipulation code (and carefully in other places too), so I need -Wno-vla. I use feature test macros (for POSIX, and also systems like Solaris) to determine how to compile code, so -Wno-reserved-id-macro was necessary. […continued…]Impoverished
[…continuation…] I needed -Wno-documentation-unknown-command was because clang undertook to critique a comment containing "** Hex character constant - \xHH or \xH, where H is a hex digit.". I needed -Wno-poison-system-directories because clang designated /usr/local/include as unsafe for cross-compilation. So, -Weverything is a blunderbuss that, for me, does not work on real-world programs (or, at least, it does not work for me in my tiny little corner of the real world). I've not run afoul of that option yet, but I've not recently checked my code thoroughly on my Mac.Impoverished
Re “One can't possibly reason about his code using this warning”: How so? One can enable the warning, compiler, and manually evaluate every place in the code it reports an error. Nothing prevents you from using reasoning on those places. Did you mean one cannot rely on every warning it produces to be an actual error in the code? That is a different thing. And it is not necessary that every warning it produces be an actual error for it to be useful in pointing out places where there are errors.Almeida
@EricPostpischil in thery, you could. In practice, any codebase will trigger thousands of warnings which overwhelms anyone trying to read through it, so if you have an actual bug exposed by one of these warnings, you won't be able to distinguish which one is it.Dill
@JonathanLeffler it might be the case that I'm just lucky, but from what you have described it seems that the diagnostics you have to disable are somewhat specific to your application's needs. Contrast that to dereferencing a pointer in C. How could anyone do something useful without it?Dill
@lucas.mior: Re “any codebase will trigger thousands of warnings”: There are software projects that are well written and well maintained that do not trigger so many warnings. That may require some level of expertise and care.Almeida
@EricPostpischil could you be more specific? Please point me to where I can find more information about these projects. I don't know of any project that compiles c code using -Wunsafe-buffer-usage, maybe there lies the answer.Dill
@lucas.mior — undoubtedly: the options I disable warnings about are specific to my needs, based on what I found (back in October 2022 for the latest update to that script), but the principle applies — disable the warnings you regard as unhelpful. I have reasons for rejecting the warnings from each option I disable. You have different code, different problems, different reasons for rejecting (or accepting) the output. But to be practically useful, you'll have to tune the warnings generated by -Weverything. My Mac has Apple's Clang 14.0; the -Wunsafe-buffer-usage option isn't valid.Impoverished
If -Wno-unsafe-buffer-usage feels like cheating, doesn't -Weverything feel like an unreasonable handicap? -Wall -Wextra is expected to be a useful set of warnings, as your quoted extract states.Trophoplasm
@lucas.mior In practice, any codebase will trigger thousands of warnings ... Bull. Every program I'm in charge of is usually completely warning-clean. Right now, the hundred-thousand-or-so-lines of C code I'm in charge of emits all of 7 warnings - only because the code is still internally using a function from the public API that was just marked deprecated in the last release a few weeks ago. That level of correctness needs a disciplined adherence to high standards. A code base that "trigger[s] thousands of warnings" was written to no or very low standards by undisciplined developers.Whitesmith
(cont) But code bases written to high standards by dedicated, disciplined, competent developers do exist. Unfortunately, Sturgeon's Law applies to writing code, too.Whitesmith
(cont) Though the -Wunsafe-buffer-usage warning does appear to be of the same ilk as Microsoft's "deprecation" of the majority of the C standard library functionality. In other words, completely useless in practice. argv is an "unsafe buffer"? I have to wonder at the thinking that came up with that. Or rather, the lack of thinking.Whitesmith
@AndrewHenle if you can point me to where -Wunsafe-buffer-usage is enabled and the compilation doesn't trigger lots of warnings, I appreciate it, since I'm trying to learn about it. Otherwise, I agree with you that code written to high standards should have no warnings, which is what I did to my code before this diagnostic was added to clang.Dill
@lucas.mior Well, the warning warning: 'argv' is an unsafe pointer used for buffer access, umm, unreasonable. If I used clang, I'd file a bug report for that "warning" so the devs would get real feedback. It's "unsafe" to access the command-line arguments of a process?!?!? That makes no sense.Whitesmith
@AndrewHenle yes, that's what the question is about. I reported it, they ignored.Dill
@TobySpeight It's a shame that there's a bunch of useful warnings that don't come with -Wall -Wextra that manually selecting them is tedious and error prone, besides adding clutter to a Makefile.Dill
I don't really find a problem with a single block of CFLAGS+= lines in my Makefile. I already have similar blocks for includes, definitions and code-generation, as well as similar blocks for linker flags and libraries. It's not clutter when it's logically organised!Trophoplasm
but it is an ugly solution. It adds clutter to a Makefile and feels like cheating. Please don't go there. It's not 'cheating' if the warning is false. I've triggered this warning even when there ARE checks. It's arguably a harmful warning because it could confuse some people into fixing code only to cause a security flaw. Disable the warning and be done with it. It's okay to ignore things that are wrong.Salina
S
14

I made several attempts at addressing this particular warning with version 16.0.0 at godbolt, i.e. checking argv != NULL && argv[1] != NULL before the call to printf, but was unsuccessful. In fact, even this particular check triggered the warning.

Given that the documentation explicitly recommends not using -Weverything and if you do to use the corresponding -Wno- option for warnings that are not useful, and that it's not clear exactly how this warning could be addressed (I'm unable to find documentation on this option), adding -Wno-unsafe-buffer-usage is probably the best option.

And based on a link in the comments from the LLVM discussion boards at https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734/85:

this warning currently fires at the one place where (afaict) there is no getting around it. int main(int argc, char const** argv)

It seems it's not possible to silence the warning in this case. So using -Wno-unsafe-buffer-usage looks to be your only option.

Sacred answered 31/8, 2023 at 16:36 Comment(13)
Yes, but the documentation says "either by fixing everything they find or explicitly disabling ..." What is the point of a warning whose code it warns against can't be fixed even in such central aspect of a program?Dill
@lucas.mior I think you may not have an option but to ignore in this case. It also seems you're not the only person to have trouble with this option.Sacred
@lucas.mior Re: "What is the point of a warning whose code it warns against can't be fixed even in such central aspect of a program" I think that's part of "experimental diagnostics" .Sacred
@lucas.mior I'm not familiar clang or the -Weverything option, but I'm curious if using int main(int argc, char *argv[]) makes a difference. Or using *++argv in the printf. Perhaps it's complaining about the mixed pointer/array syntax.Turnage
@lucas.mior: Re “What is the point of a warning whose code it warns against can't be fixed even in such central aspect of a program?”: As the documentation states, the additional warnings in -Weverything are experimental. The purpose of the experiment is to get feedback: The Clang maintainers test warning options to see how useful they are and what problems there might be with them. People using -Weverything should report problems to the Clang maintainers, including sample code that demonstrates the problems, so the Clang maintainers can consider improvements. That is the point.Almeida
@EricPostpischil "can consider improvements" is a bit questionable, discourse.llvm.org/t/rfc-c-buffer-hardening/65734/85 says this warning currently fires at "int main(int argc, char const** argv)" where (afaict) there is no getting around itPierpont
@yvs2014: So? The fact that it is reported for the argv of main is information that can be fed back to the Clang developers. Additionally, the first item at your link gives another reason for -Weverything: Use it manually once to all the diagnostics it reports, evaluate each of them yourself, fix the ones that are actual problems, but leave it off in your regular builds.Almeida
@EricPostpischil the reason to pull up this language to sound bound/null safe is good and I really like these features in other modern languages, but.. "can be" and "use it manually" doesn't sound so good to something that's already done. That's the pointPierpont
@Sacred and EricPostpischil Maybe that is the point in theory, but in practice I opened an issue about it in llvm's github and it was closed as won't fix, and the "reason" was the documentation quote I reproduced in my question (which doesn't explain why someone would want to use that warning). And this warning is still there as of clang 18.0, so I wonder if there is any actual reason for including the diagnostic besides "experimental".Dill
@yvs2014: There are people who benefit from higher scrutiny of code, for security, reliability, or other goals. If you do not feel the effort of using -Weverything in this way is a net benefit to you, then so be it. But that does not negate the existence of reasons to use it that benefit other people.Almeida
@PaulLynch I tried that, doesn't work either. It really is not about the syntax. I even tried to use variable modified types (int main(int argc, char *argc[argc])) and the result is the same.Dill
@EricPostpischil I am very much interested in learning such reasons.Dill
@lucas.mior Yeah well also static dimensions doesn't work too: int main(int argc, char *argv[const static 5]) I think the motivation should only be for C++ - and the implications are that using [ and ] on a pointer is not allowed period. (Probably you should need to use like std::span). To add - even basic array can't be indirected to - with this warning not being shown.Ethylene
D
8

This warning seems to be essentially undocumented (which perhaps is a sign that it's better disabled). The available information suggests that you haven't missed anything; it's simply not possible to compile a complete C program without triggering it. If you wish to use -Weverything with -Werror when compiling an entire C program (including main), it seems to be mandatory to disable this warning.

Thanks to @yvs2014 for this link from the comments: https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734/85

It's suggested in that discussion thread that (1) this warning is really meant for use with C++ code, which should normally not be handling raw pointers, and not for use with C code; (2) it might theoretically be used for C code which uses some library or API that abstracts away the usage of raw pointers; (but it cannot possibly be used when compiling the implementation of that library); (3) it can never be used when compiling main.

I question the wisdom of this design choice, but it is what it is (and they do pretty explicitly warn against using -Weverything.)

Door answered 1/9, 2023 at 4:38 Comment(1)
Yes, once again c++ gets in the way of C. In the link you provide, it is suggested that one writing C code should wrap the pointer arithmetic stuff in safe APIs and then use compiler directives to disable the warning in these implementations. Indeed, there is no way to do it without writing in a different language.Dill

© 2022 - 2024 — McMap. All rights reserved.