diff --git a/CMakeLists.txt b/CMakeLists.txt index e82b969410..d7f13d53ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -563,6 +563,31 @@ if(AVIF_LIB_USE_CXX OR AVIF_BUILD_APPS OR (AVIF_BUILD_TESTS AND (AVIF_FUZZTEST O enable_language(CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-Wunsafe-buffer-usage AVIF_HAVE_WUNSAFE_BUFFER_USAGE) + endif() +endif() + +# Opt a list of C++ source files into the -Wunsafe-buffer-usage diagnostic +# (the first step of adopting the Safe Buffers Programming Model). Apply only +# to .cc files that have been audited to be free of raw pointer arithmetic and +# raw-pointer indexing; new code added to those files must keep them clean. +# The libc-call subgroup (Clang 17+) is disabled because it fires on functions +# such as strdup() that are used in third-party headers (e.g. libargparse) we +# do not own; our own code avoids those by convention. +# No-op on toolchains where the flag is not available (Clang < 16, GCC, MSVC). +function(avif_enable_safe_buffers_warning) + if(AVIF_HAVE_WUNSAFE_BUFFER_USAGE) + set_source_files_properties( + ${ARGN} PROPERTIES COMPILE_OPTIONS + "-Wunsafe-buffer-usage;-Wno-unsafe-buffer-usage-in-libc-call" + ) + endif() +endfunction() + +if(AVIF_ENABLE_COMPLIANCE_WARDEN) + avif_enable_safe_buffers_warning(src/compliance.cc) endif() set_target_properties(avif_obj PROPERTIES C_VISIBILITY_PRESET hidden) @@ -723,6 +748,7 @@ if(AVIF_BUILD_APPS) apps/avifgainmaputil/program_command.cc apps/avifgainmaputil/swapbase_command.cc ) + avif_enable_safe_buffers_warning(${AVIFGAINMAPUTIL_SRCS}) add_executable(avifgainmaputil "${AVIFGAINMAPUTIL_SRCS}") if(WIN32) diff --git a/apps/avifgainmaputil/avifgainmaputil.cc b/apps/avifgainmaputil/avifgainmaputil.cc index 25ba16e7ea..dce2f360e6 100644 --- a/apps/avifgainmaputil/avifgainmaputil.cc +++ b/apps/avifgainmaputil/avifgainmaputil.cc @@ -91,6 +91,15 @@ int main(int argc, char** argv) { return 1; } + // -Wunsafe-buffer-usage doesn't know the raw pointer argv is associated with + // the bound argc, so it cannot prove these accesses safe. They are bounded + // by the argc checks and by the host's contract with main(): + // argv[0..argc-1] are valid. +#ifdef __clang__ +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang unsafe_buffer_usage begin +#endif +#endif const std::string command_name(argv[1]); if (command_name == "help") { if (argc >= 3) { @@ -129,6 +138,11 @@ int main(int argc, char** argv) { } } } +#ifdef __clang__ +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang unsafe_buffer_usage end +#endif +#endif std::cerr << "Unknown command " << command_name << "\n"; avif::PrintUsage(commands); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bb1199b7c8..9ddffbdf06 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -74,6 +74,36 @@ if(AVIF_GTEST) target_link_libraries(avifincrtest_helpers PRIVATE GTest::GTest avif_enable_warnings) endif() +# Tests that have been audited for the Safe Buffers Programming Model. Adding +# a new test to this list requires that the file compiles cleanly under +# -Wunsafe-buffer-usage. See avif_enable_safe_buffers_warning() in the root +# CMakeLists.txt. +if(AVIF_GTEST) + avif_enable_safe_buffers_warning( + gtest/avifalphapremtest.cc + gtest/avifbasictest.cc + gtest/avifcicptest.cc + gtest/avifclaptest.cc + gtest/avifcllitest.cc + gtest/avifcodectest.cc + gtest/avifcolrtest.cc + gtest/avifgridapitest.cc + gtest/avifimagetest.cc + gtest/avifopaquetest.cc + gtest/avifpropinternaltest.cc + gtest/avifsampletransformtest.cc + gtest/aviftilingtest.cc + gtest/avifutilstest.cc + gtest/avify4mtest.cc + ) + if(AVIF_CODEC_AVM_ENABLED) + avif_enable_safe_buffers_warning(gtest/avifavmtest.cc) + if(AVIF_ENABLE_EXPERIMENTAL_MINI) + avif_enable_safe_buffers_warning(gtest/avifavmminitest.cc) + endif() + endif() +endif() + if(AVIF_GTEST) add_avif_gtest_with_data(avif16bittest) add_avif_gtest(avifallocationtest)