Skip to content

Add per-source opt-in support for -Wunsafe-buffer-usage#3217

Open
metsw24-max wants to merge 4 commits into
AOMediaCodec:mainfrom
metsw24-max:unsafe-buffer-usage-optin
Open

Add per-source opt-in support for -Wunsafe-buffer-usage#3217
metsw24-max wants to merge 4 commits into
AOMediaCodec:mainfrom
metsw24-max:unsafe-buffer-usage-optin

Conversation

@metsw24-max
Copy link
Copy Markdown
Contributor

Adds infrastructure for selectively enabling Clang’s -Wunsafe-buffer-usage diagnostic on audited C++ source files.

Changes:

  • Detect compiler support for -Wunsafe-buffer-usage

  • Add avif_enable_safe_buffers_warning() helper for per-source opt-in

  • Enable the warning for:

    • src/compliance.cc
    • all apps/avifgainmaputil/*.cc
    • selected audited gtests
  • Consolidate raw argv access in avifgainmaputil.cc into a single guarded region using:

    • #ifdef __clang__
    • __has_warning("-Wunsafe-buffer-usage")
    • #pragma clang unsafe_buffer_usage begin/end

Comment thread tests/gtest/avifrgbtest.cc Outdated
// Copyright 2023 Google LLC
// SPDX-License-Identifier: BSD-2-Clause

#include <array>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

metsw24-max: Thank you for the pull request. I'd like to learn more about Clang's -Wunsafe-buffer-usage flag. Is it for C++ code only?

Could you please move this file to a separate pull request? The changes to this file could stand on their own (replacing C arrays with std::array).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @wantehchang, thanks for the review!

Regarding -Wunsafe-buffer-usage: yes, it is effectively a C++-only warning. Clang introduced it as part of the "C++ Safe Buffers" programming model. It flags unsafe pointer arithmetic and raw-pointer indexing, and its suggested fixes point at C++ types like std::span and std::array (and hardened libc++). It can technically be enabled on C translation units, but the diagnostics and fix-its don't really apply there, so we only opt in C++ sources.

Happy to split out the tests/gtest/avifrgbtest.cc changes into a separate PR
you are right that the std::array cleanup stands on its own. I'll drop it from this PR and open a follow-up.

Comment thread apps/avifgainmaputil/avifgainmaputil.cc Outdated
const std::string command_name(argv[1]);
// argv is a raw pointer with no associated bound, so -Wunsafe-buffer-usage
// cannot prove these accesses safe. They are bounded by the argc checks
// above and by the host's contract with main(): argv[0..argc-1] are valid.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: argv[argc] is also valid and is a null pointer. But most code I have seen only uses argv[0..argc-1].

Comment thread apps/avifgainmaputil/avifgainmaputil.cc Outdated
if (command->name() == command_name) {
try {
avifResult result = command->ParseArgs(argc - 1, argv + 1);
avifResult result = command->ParseArgs(argc - 1, command_argv);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: The original code has a nice symmetry between argc - 1 and argv + 1:

        avifResult result = command->ParseArgs(argc - 1, argv + 1);

This symmetry is lost in the new code. We could address this by saving argc - 1 in a new variable command_argc in conjunction with command_argv.

Comment thread apps/avifgainmaputil/avifgainmaputil.cc Outdated
#if __has_warning("-Wunsafe-buffer-usage")
#pragma clang unsafe_buffer_usage end
#endif
#endif
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

cc: @maryla-uc

Maryla: You are the author of this file. Although I expressed my opinion below, I defer the decision to you.

metsw24-max: I find the new code less clear than the original code, so the trade-off does not seem worthwhile for this main() function.

An experienced programmer can easily see this main() function uses the argv array safely by searching for "argv" and verifying argv is only used with an index less than argc.

Here you annotated three accesses to argv as unsafe. Is it possible to simply annotate the argv variable as unsafe?

Note: Clang's Safe C++ Buffers page recommends handling this kind of case using C++20's std:: span. But libavif follows Google's Foundational C++ Support table and needs to stick with C++17 until 2027-12-15.

Comment thread apps/avifgainmaputil/avifgainmaputil.cc Outdated
}

const std::string command_name(argv[1]);
// argv is a raw pointer with no associated bound, so -Wunsafe-buffer-usage
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: argv is associated with the bound argc. It's just that -Wunsafe-buffer-usage doesn't understand this association. I suggest changing this comment to "-Wunsafe-buffer-usage doesn't know the raw pointer argv is associated with the bound argc, so it cannot prove these accesses safe."

Comment thread apps/avifgainmaputil/avifgainmaputil.cc Outdated
const std::string command_name(argv[1]);
// argv is a raw pointer with no associated bound, so -Wunsafe-buffer-usage
// cannot prove these accesses safe. They are bounded by the argc checks
// above and by the host's contract with main(): argv[0..argc-1] are valid.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: Remove "above" from "the argc checks above", because the argc >= 3 check is below.

@metsw24-max
Copy link
Copy Markdown
Contributor Author

metsw24-max commented May 21, 2026

Thanks @wantehchang for the detailed feedback. I have pushed an update that:

  • Reverts the command_cstr / sub_command_cstr / command_argv consolidation, restoring the original code structure (so argv[1], argv[2], and argv + 1 are used inline as before, with the argc - 1 / argv + 1 symmetry intact).

  • Wraps the relevant block of main() in a single #pragma clang unsafe_buffer_usage region. To your question about annotating argv directly: Clang doesn't currently expose a per-variable attribute for this; [[clang::unsafe_buffer_usage]] only applies to functions, and std::span would require C++20. The pragma block over the function body is the cleanest pre-C++20 option I am aware of, happy to switch to a different approach if you or @maryla-uc have one in mind.

  • Updated the comment per your suggestions (kept "argv[0..argc-1]", removed "above", and corrected the bound-association wording).

The avifrgbtest.cc changes have been removed from this PR and will be raised as a follow-up

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.

2 participants