Adding -Wformat-nonliteral check (#12763)

* Turn on -Wformat-security and -Wformat-nonliteral.

The ENFORCE_FORMAT annotations help make it clear to the compiler
which seemingly-nonliteral values are actually checked to be literals
further up the callstack, because we disallow passing a non-literal as
the format arg indicated by ENFORCE_FORMAT.

* Address review comments

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
diff --git a/.github/workflows/examples-esp32.yaml b/.github/workflows/examples-esp32.yaml
index 023d1b2..54298cf 100644
--- a/.github/workflows/examples-esp32.yaml
+++ b/.github/workflows/examples-esp32.yaml
@@ -26,7 +26,7 @@
     # TODO ESP32 https://github.com/project-chip/connectedhomeip/issues/1510
     esp32:
         name: ESP32
-        timeout-minutes: 85
+        timeout-minutes: 95
 
         runs-on: ubuntu-latest
         if: github.actor != 'restyled-io[bot]'
@@ -60,7 +60,7 @@
                    .environment/gn_out/.ninja_log
                    .environment/pigweed-venv/*.log
             - name: Build some M5Stack variations
-              timeout-minutes: 20
+              timeout-minutes: 30
               run: |
                   ./scripts/run_in_build_env.sh \
                      "./scripts/build/build_examples.py \
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 81fb08c..9df167b 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -202,6 +202,9 @@
     "-Wshadow",
     "-Wunreachable-code",
     "-Wvla",
+    "-Wformat",
+    "-Wformat-nonliteral",
+    "-Wformat-security",
   ]
 
   cflags_cc = [ "-Wnon-virtual-dtor" ]
diff --git a/config/ameba/chip.cmake b/config/ameba/chip.cmake
index 9d39d92..b7986d5 100644
--- a/config/ameba/chip.cmake
+++ b/config/ameba/chip.cmake
@@ -42,6 +42,8 @@
     -Wno-unused-parameter
     -Wno-format
     -Wno-stringop-truncation
+    -Wno-format-nonliteral
+    -Wno-format-security
     -std=c++17
 )
 
diff --git a/examples/all-clusters-app/esp32/CMakeLists.txt b/examples/all-clusters-app/esp32/CMakeLists.txt
index 9685eac..3753bdd 100644
--- a/examples/all-clusters-app/esp32/CMakeLists.txt
+++ b/examples/all-clusters-app/esp32/CMakeLists.txt
@@ -34,6 +34,9 @@
 project(chip-all-clusters-app)
 idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DLWIP_IPV6_SCOPES=0;-DCHIP_HAVE_CONFIG_H" APPEND)
 idf_build_set_property(C_COMPILE_OPTIONS "-Os;-DLWIP_IPV6_SCOPES=0" APPEND)
+# For the C3, project_include.cmake sets -Wno-format, but does not clear various
+# flags that depend on -Wformat
+idf_build_set_property(COMPILE_OPTIONS "-Wno-format-nonliteral;-Wno-format-security" APPEND)
 
 flashing_script()
 
diff --git a/examples/platform/telink/util/src/ThreadUtil.cpp b/examples/platform/telink/util/src/ThreadUtil.cpp
index c501cd9..bbe95d8 100644
--- a/examples/platform/telink/util/src/ThreadUtil.cpp
+++ b/examples/platform/telink/util/src/ThreadUtil.cpp
@@ -51,7 +51,7 @@
     chip::DeviceLayer::ThreadStackMgr().SetThreadEnabled(true);
 }
 
-void tlOtPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char * aFormat, ...)
+void ENFORCE_FORMAT(3, 4) tlOtPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char * aFormat, ...)
 {
     va_list args;
 
diff --git a/src/app/MessageDef/MessageDefHelper.cpp b/src/app/MessageDef/MessageDefHelper.cpp
index a8f6c33..f57feed 100644
--- a/src/app/MessageDef/MessageDefHelper.cpp
+++ b/src/app/MessageDef/MessageDefHelper.cpp
@@ -41,7 +41,7 @@
 size_t gCurLineBufferSize = 0;
 } // namespace
 
-void PrettyPrintIM(bool aIsNewLine, const char * aFmt, ...)
+void ENFORCE_FORMAT(2, 3) PrettyPrintIM(bool aIsNewLine, const char * aFmt, ...)
 {
     va_list args;
     size_t ret;
diff --git a/src/app/tests/TestEventLogging.cpp b/src/app/tests/TestEventLogging.cpp
index 016df16..97f152e 100644
--- a/src/app/tests/TestEventLogging.cpp
+++ b/src/app/tests/TestEventLogging.cpp
@@ -32,8 +32,10 @@
 #include <lib/core/CHIPTLV.h>
 #include <lib/core/CHIPTLVDebug.hpp>
 #include <lib/core/CHIPTLVUtilities.hpp>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/ErrorStr.h>
 #include <lib/support/UnitTestRegistration.h>
+#include <lib/support/logging/Constants.h>
 #include <messaging/ExchangeContext.h>
 #include <messaging/Flags.h>
 #include <platform/CHIPDeviceLayer.h>
@@ -89,7 +91,7 @@
     }
 };
 
-void SimpleDumpWriter(const char * aFormat, ...)
+void ENFORCE_FORMAT(1, 2) SimpleDumpWriter(const char * aFormat, ...)
 {
     va_list args;
 
diff --git a/src/app/tests/TestMessageDef.cpp b/src/app/tests/TestMessageDef.cpp
index 9451e20..95c3cd5 100644
--- a/src/app/tests/TestMessageDef.cpp
+++ b/src/app/tests/TestMessageDef.cpp
@@ -37,7 +37,9 @@
 #include <lib/core/CHIPError.h>
 #include <lib/core/CHIPTLVDebug.hpp>
 #include <lib/support/CHIPMem.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/UnitTestRegistration.h>
+#include <lib/support/logging/Constants.h>
 #include <system/TLVPacketBufferBackingStore.h>
 
 #include <nlunit-test.h>
@@ -46,7 +48,7 @@
 
 using namespace chip::app;
 
-void TLVPrettyPrinter(const char * aFormat, ...)
+void ENFORCE_FORMAT(1, 2) TLVPrettyPrinter(const char * aFormat, ...)
 {
     va_list args;
 
diff --git a/src/app/tests/integration/common.cpp b/src/app/tests/integration/common.cpp
index d0ad7da..d898928 100644
--- a/src/app/tests/integration/common.cpp
+++ b/src/app/tests/integration/common.cpp
@@ -27,7 +27,9 @@
 #include <app/tests/integration/common.h>
 #include <lib/core/CHIPCore.h>
 #include <lib/core/CHIPTLVDebug.hpp>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/ErrorStr.h>
+#include <lib/support/logging/Constants.h>
 #include <platform/CHIPDeviceLayer.h>
 
 chip::Messaging::ExchangeManager gExchangeManager;
@@ -65,7 +67,7 @@
     chip::DeviceLayer::PlatformMgr().Shutdown();
 }
 
-void TLVPrettyPrinter(const char * aFormat, ...)
+void ENFORCE_FORMAT(1, 2) TLVPrettyPrinter(const char * aFormat, ...)
 {
     va_list args;
 
diff --git a/src/app/util/ember-print.cpp b/src/app/util/ember-print.cpp
index 3f21c53..51326ca 100644
--- a/src/app/util/ember-print.cpp
+++ b/src/app/util/ember-print.cpp
@@ -76,7 +76,14 @@
             const uint32_t outStringEnd   = segmentLength * perByteCharCount;
             for (uint32_t dst_idx = 0; dst_idx < outStringEnd && index < segmentEnd; dst_idx += perByteCharCount, index++)
             {
+                // The perByteFormatStr is in fact a literal (one of two), but
+                // the compiler does not realize that.  We could branch on
+                // perByteFormatStr and have separate snprintf calls, but this
+                // seems like it might lead to smaller code.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
                 snprintf(result + dst_idx, outStringEnd - dst_idx + 1, perByteFormatStr, buffer[index]);
+#pragma GCC diagnostic pop
             }
             result[outStringEnd] = 0;
             emberAfPrint(category, "%s", result);
diff --git a/src/controller/python/chip/logging/LoggingRedirect.cpp b/src/controller/python/chip/logging/LoggingRedirect.cpp
index e66f663..c290fa3 100644
--- a/src/controller/python/chip/logging/LoggingRedirect.cpp
+++ b/src/controller/python/chip/logging/LoggingRedirect.cpp
@@ -26,7 +26,7 @@
 
 PythonLogCallback sPythonLogCallback;
 
-void NativeLoggingCallback(const char * module, uint8_t category, const char * msg, va_list args)
+void ENFORCE_FORMAT(3, 0) NativeLoggingCallback(const char * module, uint8_t category, const char * msg, va_list args)
 {
     if (sPythonLogCallback == nullptr)
     {
diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
index 312dd83..00dd58d 100644
--- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
+++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
@@ -663,6 +663,11 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.4;
 				LIBRARY_SEARCH_PATHS = "$(TEMP_DIR)/out/lib";
+				OTHER_CFLAGS = (
+					"-Wformat",
+					"-Wformat-nonliteral",
+					"-Wformat-security",
+				);
 				OTHER_LDFLAGS = "";
 				"OTHER_LDFLAGS[sdk=*]" = (
 					"-framework",
@@ -800,6 +805,11 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.4;
 				LIBRARY_SEARCH_PATHS = "$(TEMP_DIR)/out/lib";
+				OTHER_CFLAGS = (
+					"-Wformat",
+					"-Wformat-nonliteral",
+					"-Wformat-security",
+				);
 				OTHER_LDFLAGS = "";
 				"OTHER_LDFLAGS[arch=*]" = (
 					"-lnetwork",
diff --git a/src/lib/core/CHIPTLV.h b/src/lib/core/CHIPTLV.h
index 1410493..9f3947c 100644
--- a/src/lib/core/CHIPTLV.h
+++ b/src/lib/core/CHIPTLV.h
@@ -33,9 +33,11 @@
 
 #include <lib/support/BitFlags.h>
 #include <lib/support/DLLUtil.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/ScopedBuffer.h>
 #include <lib/support/Span.h>
 #include <lib/support/TypeTraits.h>
+#include <lib/support/logging/Constants.h>
 
 #include <stdarg.h>
 #include <stdlib.h>
@@ -1614,7 +1616,9 @@
      *               `WriteElementHead` or `GetNewBuffer` -- failed, their
      *               error is immediately forwarded up the call stack.
      */
-    CHIP_ERROR PutStringF(Tag tag, const char * fmt, ...);
+    // The ENFORCE_FORMAT args are "off by one" because this is a class method,
+    // with an implicit "this" as first arg.
+    CHIP_ERROR PutStringF(Tag tag, const char * fmt, ...) ENFORCE_FORMAT(3, 4);
 
     /**
      * @brief
@@ -1660,7 +1664,9 @@
      *               `WriteElementHead` or `GetNewBuffer` -- failed, their
      *               error is immediately forwarded up the call stack.
      */
-    CHIP_ERROR VPutStringF(Tag tag, const char * fmt, va_list ap);
+    // The ENFORCE_FORMAT args are "off by one" because this is a class method,
+    // with an implicit "this" as first arg.
+    CHIP_ERROR VPutStringF(Tag tag, const char * fmt, va_list ap) ENFORCE_FORMAT(3, 0);
 
     /**
      * Encodes a TLV null value.
diff --git a/src/lib/core/tests/TestCHIPTLV.cpp b/src/lib/core/tests/TestCHIPTLV.cpp
index 112a721..3cc0800 100644
--- a/src/lib/core/tests/TestCHIPTLV.cpp
+++ b/src/lib/core/tests/TestCHIPTLV.cpp
@@ -38,6 +38,7 @@
 #include <lib/support/ScopedBuffer.h>
 #include <lib/support/UnitTestRegistration.h>
 #include <lib/support/UnitTestUtils.h>
+#include <lib/support/logging/Constants.h>
 
 #include <system/TLVPacketBufferBackingStore.h>
 
@@ -1632,7 +1633,7 @@
  *                           to the format specifiers in @a aFormat.
  *
  */
-void SimpleDumpWriter(const char * aFormat, ...)
+void ENFORCE_FORMAT(1, 2) SimpleDumpWriter(const char * aFormat, ...)
 {
     va_list args;
 
diff --git a/src/lib/dnssd/Discovery_ImplPlatform.cpp b/src/lib/dnssd/Discovery_ImplPlatform.cpp
index ca32481..916bf27 100644
--- a/src/lib/dnssd/Discovery_ImplPlatform.cpp
+++ b/src/lib/dnssd/Discovery_ImplPlatform.cpp
@@ -174,7 +174,8 @@
     return AddPtrRecord(type, entries, entriesCount, buffer, bufferLen, value.Value());
 }
 
-CHIP_ERROR CopyTextRecordValue(char * buffer, size_t bufferLen, int minCharactersWritten, const char * format, ...)
+CHIP_ERROR ENFORCE_FORMAT(4, 5)
+    CopyTextRecordValue(char * buffer, size_t bufferLen, int minCharactersWritten, const char * format, ...)
 {
     va_list args;
     va_start(args, format);
diff --git a/src/lib/shell/streamer.cpp b/src/lib/shell/streamer.cpp
index 4bb11eb..852688e 100644
--- a/src/lib/shell/streamer.cpp
+++ b/src/lib/shell/streamer.cpp
@@ -23,6 +23,8 @@
 
 #include "streamer.h"
 
+#include <lib/support/EnforceFormat.h>
+#include <lib/support/logging/Constants.h>
 #include <limits.h>
 #include <stdio.h>
 
@@ -48,7 +50,7 @@
     return self->write_cb(self, buf, len);
 }
 
-ssize_t streamer_vprintf(streamer_t * self, const char * fmt, va_list ap)
+ssize_t ENFORCE_FORMAT(2, 0) streamer_vprintf(streamer_t * self, const char * fmt, va_list ap)
 {
     char buf[CONSOLE_DEFAULT_MAX_LINE];
     unsigned len;
@@ -64,7 +66,7 @@
     return streamer_write(self, buf, len);
 }
 
-ssize_t streamer_printf(streamer_t * self, const char * fmt, ...)
+ssize_t ENFORCE_FORMAT(2, 3) streamer_printf(streamer_t * self, const char * fmt, ...)
 {
     va_list ap;
     ssize_t rc;
diff --git a/src/lib/support/BUILD.gn b/src/lib/support/BUILD.gn
index fe8c92d..39b83c3 100644
--- a/src/lib/support/BUILD.gn
+++ b/src/lib/support/BUILD.gn
@@ -40,6 +40,10 @@
   sources = [ "logging/Constants.h" ]
 }
 
+source_set("enforce_format") {
+  sources = [ "EnforceFormat.h" ]
+}
+
 source_set("chip_version_header") {
   sources = get_target_outputs(":gen_chip_version")
 
@@ -131,6 +135,7 @@
 
   public_deps = [
     ":chip_version_header",
+    ":enforce_format",
     ":logging_constants",
     "${chip_root}/src/lib/core:chip_config_header",
     "${chip_root}/src/platform:platform_buildconfig",
diff --git a/src/lib/support/CHIPArgParser.cpp b/src/lib/support/CHIPArgParser.cpp
index a4384ba..6fd0858 100644
--- a/src/lib/support/CHIPArgParser.cpp
+++ b/src/lib/support/CHIPArgParser.cpp
@@ -48,6 +48,8 @@
 
 #include <lib/support/CHIPMem.h>
 #include <lib/support/CHIPMemString.h>
+#include <lib/support/EnforceFormat.h>
+#include <lib/support/logging/Constants.h>
 
 /*
  * TODO: Revisit these if and when fabric ID and node ID support has
@@ -622,7 +624,7 @@
  * Applications should call through the PrintArgError function pointer, rather
  * than calling this function directly.
  */
-void DefaultPrintArgError(const char * msg, ...)
+void ENFORCE_FORMAT(1, 2) DefaultPrintArgError(const char * msg, ...)
 {
     va_list ap;
 
diff --git a/src/lib/support/DefaultStorageKeyAllocator.h b/src/lib/support/DefaultStorageKeyAllocator.h
index 4a8ada7..c469f04 100644
--- a/src/lib/support/DefaultStorageKeyAllocator.h
+++ b/src/lib/support/DefaultStorageKeyAllocator.h
@@ -17,6 +17,8 @@
 #pragma once
 
 #include <app/util/basic-types.h>
+#include <lib/support/EnforceFormat.h>
+#include <lib/support/logging/Constants.h>
 #include <string.h>
 
 namespace chip {
@@ -48,7 +50,9 @@
 private:
     static const size_t kKeyLengthMax = 32;
 
-    const char * Format(const char * format...)
+    // The ENFORCE_FORMAT args are "off by one" because this is a class method,
+    // with an implicit "this" as first arg.
+    const char * ENFORCE_FORMAT(2, 3) Format(const char * format, ...)
     {
         va_list args;
         va_start(args, format);
diff --git a/src/lib/support/EnforceFormat.h b/src/lib/support/EnforceFormat.h
new file mode 100644
index 0000000..7d8f681
--- /dev/null
+++ b/src/lib/support/EnforceFormat.h
@@ -0,0 +1,34 @@
+/*
+ *    Copyright (c) 2022 Project CHIP Authors
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#pragma once
+
+/**
+ * gcc and clang provide a way to warn for a custom formatter when formats don't
+ * match arguments.  Use that so we catch mistakes.  The "format"
+ * attribute takes the type of format, which arg is the format string, and which
+ * arg is the first variadic arg, with both arg numbers 1-based.
+ *
+ * The second arg should be set to 0 if the function takes a va_list instead of
+ * varargs.
+ */
+
+#if defined(__GNUC__)
+#define ENFORCE_FORMAT(n, m) __attribute__((format(printf, n, m)))
+#else                        // __GNUC__
+#define ENFORCE_FORMAT(n, m) /* How to do with MSVC? */
+#endif                       // __GNUC__
diff --git a/src/lib/support/logging/CHIPLogging.h b/src/lib/support/logging/CHIPLogging.h
index 8dfffd2..fa48aa4 100644
--- a/src/lib/support/logging/CHIPLogging.h
+++ b/src/lib/support/logging/CHIPLogging.h
@@ -39,6 +39,7 @@
 
 #include <platform/logging/LogV.h>
 
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 
 #include <inttypes.h>
@@ -79,20 +80,7 @@
 
 void SetLogRedirectCallback(LogRedirectCallback_t callback);
 
-/**
- * gcc and clang provide a way to warn for a custom formatter when formats don't
- * match arguments.  Use that for Log() so we catch mistakes.  The "format"
- * attribute takes the type of format, which arg is the format string, and which
- * arg is the first variadic arg, with both arg numbers 1-based.
- */
-
-#if defined(__GNUC__)
-#define ENFORCE_FORMAT(n, m) __attribute__((format(printf, n, m)))
-#else                        // __GNUC__
-#define ENFORCE_FORMAT(n, m) /* How to do with MSVC? */
-#endif                       // __GNUC__
-
-void LogV(uint8_t module, uint8_t category, const char * msg, va_list args);
+void LogV(uint8_t module, uint8_t category, const char * msg, va_list args) ENFORCE_FORMAT(3, 0);
 void Log(uint8_t module, uint8_t category, const char * msg, ...) ENFORCE_FORMAT(3, 4);
 
 void LogByteSpan(uint8_t module, uint8_t category, const ByteSpan & span);
diff --git a/src/lib/support/tests/TestCHIPArgParser.cpp b/src/lib/support/tests/TestCHIPArgParser.cpp
index 128b5d7..4ba1f9f 100644
--- a/src/lib/support/tests/TestCHIPArgParser.cpp
+++ b/src/lib/support/tests/TestCHIPArgParser.cpp
@@ -26,8 +26,10 @@
 #include <lib/support/CHIPArgParser.hpp>
 #include <lib/support/CHIPMem.h>
 #include <lib/support/CHIPMemString.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/ScopedBuffer.h>
 #include <lib/support/UnitTestRegistration.h>
+#include <lib/support/logging/Constants.h>
 
 #if CHIP_CONFIG_ENABLE_ARG_PARSER
 
@@ -730,7 +732,7 @@
     return true;
 }
 
-static void HandleArgError(const char * msg, ...)
+static void ENFORCE_FORMAT(1, 2) HandleArgError(const char * msg, ...)
 {
     size_t msgLen;
     int status;
diff --git a/src/platform/Darwin/Logging.cpp b/src/platform/Darwin/Logging.cpp
index 180ec87..8e25d6a 100644
--- a/src/platform/Darwin/Logging.cpp
+++ b/src/platform/Darwin/Logging.cpp
@@ -1,10 +1,10 @@
 /* See Project chip LICENSE file for licensing information. */
 
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 #include <platform/logging/LogV.h>
 
 #include <lib/core/CHIPConfig.h>
-#include <lib/support/logging/Constants.h>
 
 #include <os/log.h>
 
@@ -19,7 +19,7 @@
 namespace Logging {
 namespace Platform {
 
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     timeval time;
     gettimeofday(&time, NULL);
diff --git a/src/platform/ESP32/Logging.cpp b/src/platform/ESP32/Logging.cpp
index a6f7aca..ee22dc4 100644
--- a/src/platform/ESP32/Logging.cpp
+++ b/src/platform/ESP32/Logging.cpp
@@ -3,6 +3,7 @@
 #include <platform/logging/LogV.h>
 
 #include <lib/core/CHIPConfig.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 
 #include <stdio.h>
@@ -18,7 +19,7 @@
 namespace Logging {
 namespace Platform {
 
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     char tag[11];
 
diff --git a/src/platform/Linux/Logging.cpp b/src/platform/Linux/Logging.cpp
index b5a15f5..5f8aeb5 100644
--- a/src/platform/Linux/Logging.cpp
+++ b/src/platform/Linux/Logging.cpp
@@ -1,5 +1,7 @@
 /* See Project CHIP LICENSE file for licensing information. */
 
+#include <lib/support/EnforceFormat.h>
+#include <lib/support/logging/Constants.h>
 #include <platform/logging/LogV.h>
 
 #include <cinttypes>
@@ -27,7 +29,7 @@
 /**
  * CHIP log output functions.
  */
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     struct timeval tv;
 
diff --git a/src/platform/Tizen/Logging.cpp b/src/platform/Tizen/Logging.cpp
index 7c8865f..4c5c8b8 100644
--- a/src/platform/Tizen/Logging.cpp
+++ b/src/platform/Tizen/Logging.cpp
@@ -18,6 +18,7 @@
 #include <platform/logging/LogV.h>
 
 #include <lib/core/CHIPConfig.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 
 #include <dlog.h>
@@ -30,7 +31,7 @@
 /**
  * CHIP log output functions.
  */
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     constexpr const char * kLogTag                = "CHIP";
     char msgBuf[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE] = {
diff --git a/src/platform/Zephyr/Logging.cpp b/src/platform/Zephyr/Logging.cpp
index 017ed0f..5fb3b98 100644
--- a/src/platform/Zephyr/Logging.cpp
+++ b/src/platform/Zephyr/Logging.cpp
@@ -3,6 +3,7 @@
 #include <platform/logging/LogV.h>
 
 #include <lib/core/CHIPConfig.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 
 #include <kernel.h>
@@ -40,7 +41,7 @@
  * CHIP log output function.
  */
 
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     char formattedMsg[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
     snprintfcb(formattedMsg, sizeof(formattedMsg), "[%s]", module);
diff --git a/src/platform/logging/BUILD.gn b/src/platform/logging/BUILD.gn
index bfece14..038f942 100644
--- a/src/platform/logging/BUILD.gn
+++ b/src/platform/logging/BUILD.gn
@@ -16,6 +16,7 @@
     deps = [
       ":headers",
       "${chip_root}/src/lib/core:chip_config_header",
+      "${chip_root}/src/lib/support:enforce_format",
       "${chip_root}/src/lib/support:logging_constants",
       "${chip_root}/src/platform:platform_buildconfig",
     ]
@@ -30,6 +31,7 @@
   deps = [
     ":headers",
     "${chip_root}/src/lib/core:chip_config_header",
+    "${chip_root}/src/lib/support:enforce_format",
     "${chip_root}/src/lib/support:logging_constants",
     "${chip_root}/src/platform:platform_buildconfig",
   ]
diff --git a/src/platform/logging/LogV.h b/src/platform/logging/LogV.h
index 5b961b7..5bff38f 100644
--- a/src/platform/logging/LogV.h
+++ b/src/platform/logging/LogV.h
@@ -2,6 +2,7 @@
 
 #pragma once
 
+#include <lib/support/EnforceFormat.h>
 #include <stdarg.h>
 #include <stdint.h>
 
@@ -27,7 +28,7 @@
  *                      correspond to the format specifiers in @a msg.
  *
  */
-void LogV(const char * module, uint8_t category, const char * msg, va_list v);
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v);
 
 } // namespace Platform
 } // namespace Logging
diff --git a/src/platform/logging/impl/android/Logging.cpp b/src/platform/logging/impl/android/Logging.cpp
index fcfc397..fc64331 100644
--- a/src/platform/logging/impl/android/Logging.cpp
+++ b/src/platform/logging/impl/android/Logging.cpp
@@ -1,5 +1,6 @@
 /* See Project chip LICENSE file for licensing information. */
 
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 #include <platform/logging/LogV.h>
 
@@ -9,7 +10,7 @@
 namespace Logging {
 namespace Platform {
 
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     int priority = (category == kLogCategory_Error) ? ANDROID_LOG_ERROR : ANDROID_LOG_DEBUG;
     __android_log_vprint(priority, module, msg, v);
diff --git a/src/platform/logging/impl/stdio/Logging.cpp b/src/platform/logging/impl/stdio/Logging.cpp
index 95d2093..6eceebb 100644
--- a/src/platform/logging/impl/stdio/Logging.cpp
+++ b/src/platform/logging/impl/stdio/Logging.cpp
@@ -1,5 +1,7 @@
 /* See Project CHIP LICENSE file for licensing information. */
 
+#include <lib/support/EnforceFormat.h>
+#include <lib/support/logging/Constants.h>
 #include <platform/logging/LogV.h>
 
 #include <stdio.h>
@@ -8,7 +10,7 @@
 namespace Logging {
 namespace Platform {
 
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     printf("CHIP:%s: ", module);
     vprintf(msg, v);
diff --git a/src/platform/mbed/Logging.cpp b/src/platform/mbed/Logging.cpp
index 0da70e4..3061585 100644
--- a/src/platform/mbed/Logging.cpp
+++ b/src/platform/mbed/Logging.cpp
@@ -22,6 +22,7 @@
  */
 
 #include <lib/core/CHIPConfig.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/CHIPLogging.h>
 #include <lib/support/logging/Constants.h>
 #include <platform/logging/LogV.h>
@@ -65,7 +66,7 @@
 /**
  * CHIP log output functions.
  */
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     size_t prefixLen = 0;
     snprintf(logMsgBuffer, sizeof(logMsgBuffer), "[%s]", module);
diff --git a/src/platform/nxp/k32w/k32w0/Logging.cpp b/src/platform/nxp/k32w/k32w0/Logging.cpp
index bf5bc98..69db581 100644
--- a/src/platform/nxp/k32w/k32w0/Logging.cpp
+++ b/src/platform/nxp/k32w/k32w0/Logging.cpp
@@ -3,6 +3,7 @@
 #include <platform/logging/LogV.h>
 
 #include <lib/core/CHIPConfig.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 #include <platform/CHIPDeviceConfig.h>
 #include <src/lib/support/CodeUtils.h>
@@ -96,7 +97,7 @@
 } // namespace DeviceLayer
 } // namespace chip
 
-void GenericLog(const char * format, va_list arg, const char * module, uint8_t category)
+void ENFORCE_FORMAT(1, 0) GenericLog(const char * format, va_list arg, const char * module, uint8_t category)
 {
 
 #if K32W_LOG_ENABLED
@@ -135,7 +136,7 @@
 /**
  * CHIP log output function.
  */
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     (void) module;
     (void) category;
diff --git a/src/platform/qpg/Logging.cpp b/src/platform/qpg/Logging.cpp
index f5f346e..b9fb6b4 100644
--- a/src/platform/qpg/Logging.cpp
+++ b/src/platform/qpg/Logging.cpp
@@ -5,6 +5,7 @@
 
 #include <lib/core/CHIPConfig.h>
 #include <lib/support/CHIPPlatformMemory.h>
+#include <lib/support/EnforceFormat.h>
 #include <lib/support/logging/Constants.h>
 #include <platform/CHIPDeviceConfig.h>
 
@@ -42,7 +43,7 @@
  * CHIP log output function.
  */
 
-void LogV(const char * module, uint8_t category, const char * msg, va_list v)
+void ENFORCE_FORMAT(3, 0) LogV(const char * module, uint8_t category, const char * msg, va_list v)
 {
     char formattedMsg[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
     size_t prefixLen;
diff --git a/third_party/mbedtls/mbedtls.gni b/third_party/mbedtls/mbedtls.gni
index bea9519..9bde362 100644
--- a/third_party/mbedtls/mbedtls.gni
+++ b/third_party/mbedtls/mbedtls.gni
@@ -24,6 +24,7 @@
       "-Wno-maybe-uninitialized",
       "-Wno-string-concatenation",
       "-Wno-unused-but-set-parameter",
+      "-Wno-format-nonliteral",  # Because of mbedtls_debug_print_msg
     ]
   }