]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
setlocale: Detect invalid writes to the returned string in some cases.
authorBruno Haible <bruno@clisp.org>
Sun, 7 Jun 2026 12:57:38 +0000 (14:57 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 7 Jun 2026 12:57:38 +0000 (14:57 +0200)
* lib/locale.in.h (setlocale): Change the return type to 'const char *'.
* lib/setlocale.c (setlocale_mtsafe, setlocale_unixlike): Change the
return type to 'const char *'. Remove a cast.
(setlocale_single): Change the return type to 'const char *'.
(setlocale_improved): Change the return type to 'const char *'. Change
three variables from 'char *' to 'const char *'. Add local variable
old_locale.
* lib/localcharset.c (locale_charset): Change two variables from
'char *' to 'const char *'.
* doc/posix-functions/setlocale.texi: Document that Gnulib uses the
return type 'const char *'.
* NEWS: Mention the change.
* m4/mbrtowc.m4 (gl_MBRTOWC_C_LOCALE): Change a variable from 'char *'
to 'const char *'.
* m4/mbrtoc32.m4 (gl_MBRTOC32_C_LOCALE, gl_MBRTOC32_UTF8_LOCALE):
Likewise.
* m4/mbrtoc16.m4 (gl_MBRTOC16_C_LOCALE): Likewise.
* tests/test-setlocale-w32utf8.c (main): Likewise.
* tests/test-setlocale1.c (setlocale): Update expected signature.
* tests/test-locale-h-c++.cc (GNULIB_NAMESPACE::setlocale): Likewise.

12 files changed:
ChangeLog
NEWS
doc/posix-functions/setlocale.texi
lib/localcharset.c
lib/locale.in.h
lib/setlocale.c
m4/mbrtoc16.m4
m4/mbrtoc32.m4
m4/mbrtowc.m4
tests/test-locale-h-c++.cc
tests/test-setlocale-w32utf8.c
tests/test-setlocale1.c

index 8f97ecaf8eae99fa704f9cad019d38bec7e84727..e8b41a636d56658953a83d3dcf9dda9956f68b47 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2026-06-07  Bruno Haible  <bruno@clisp.org>
+
+       setlocale: Detect invalid writes to the returned string in some cases.
+       * lib/locale.in.h (setlocale): Change the return type to 'const char *'.
+       * lib/setlocale.c (setlocale_mtsafe, setlocale_unixlike): Change the
+       return type to 'const char *'. Remove a cast.
+       (setlocale_single): Change the return type to 'const char *'.
+       (setlocale_improved): Change the return type to 'const char *'. Change
+       three variables from 'char *' to 'const char *'. Add local variable
+       old_locale.
+       * lib/localcharset.c (locale_charset): Change two variables from
+       'char *' to 'const char *'.
+       * doc/posix-functions/setlocale.texi: Document that Gnulib uses the
+       return type 'const char *'.
+       * NEWS: Mention the change.
+       * m4/mbrtowc.m4 (gl_MBRTOWC_C_LOCALE): Change a variable from 'char *'
+       to 'const char *'.
+       * m4/mbrtoc32.m4 (gl_MBRTOC32_C_LOCALE, gl_MBRTOC32_UTF8_LOCALE):
+       Likewise.
+       * m4/mbrtoc16.m4 (gl_MBRTOC16_C_LOCALE): Likewise.
+       * tests/test-setlocale-w32utf8.c (main): Likewise.
+       * tests/test-setlocale1.c (setlocale): Update expected signature.
+       * tests/test-locale-h-c++.cc (GNULIB_NAMESPACE::setlocale): Likewise.
+
 2026-06-07  Bruno Haible  <bruno@clisp.org>
 
        tests: Avoid conflict between mingw standard C++ library and Gnulib.
diff --git a/NEWS b/NEWS
index faaece85b2eb57bc54efe22bf645388ade81ff30..9369ba9acc01bb350f54f297e40fdce2108f97d0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -78,6 +78,13 @@ User visible incompatible changes
 
 Date        Modules         Changes
 
+2026-06-07  setlocale       The return type of the setlocale function is now
+                            'const char *' on some platforms.  You may need to
+                            adjust assignments of the form
+                              char *l = setlocale (...);
+                            to
+                              const char *l = setlocale (...);
+
 2026-03-31  lock            This module no longer defines the once-only
                             execution primitives. For these, request the
                             'once' module and #include "glthread/once.h".
index ecc342a47602b298f7af923b55b2ca1bcdbf813a..25f8a9500adaa48e70f21dce2185743f8a965064 100644 (file)
@@ -65,6 +65,13 @@ the @code{"C"} or @code{"POSIX"} locale actually sets an equivalent of the
 @code{"C.UTF-8"} locale.
 @end itemize
 
+Note: The return type of the @code{setlocale} function
+is @code{char *} in POSIX, with the constraint that
+``The application shall not modify the string returned.''
+The Gnulib-overridden @code{setlocale} function
+has a return type of @code{const char *}.
+This helps detecting code that attempts to write into that string.
+
 Note: The names of locales with UTF-8 encoding are platform dependent:
 @itemize
 @item
index 48409137f4f230678425e97d7b3867ba0d1fcb55..7c00d078d925a09a429db426ba474d6250718bfc 100644 (file)
@@ -882,8 +882,8 @@ locale_charset (void)
      'setlocale' call specified.  So we use it as a last resort, in
      case the string returned by 'setlocale' doesn't specify the
      codepage.  */
-  char *current_locale = setlocale (LC_CTYPE, NULL);
-  char *pdot = strrchr (current_locale, '.');
+  const char *current_locale = setlocale (LC_CTYPE, NULL);
+  const char *pdot = strrchr (current_locale, '.');
 
   if (pdot && 2 + strlen (pdot + 1) + 1 <= sizeof (buf))
     sprintf (buf, "CP%s", pdot + 1);
index e529111e487500796f87ba39f5df144d45584316..7db56214524ec8e4d748667aeaf2c78198b11aa3 100644 (file)
@@ -272,16 +272,21 @@ _GL_WARN_ON_USE (localeconv,
 #endif
 
 #if @GNULIB_SETLOCALE@
+/* The return type 'const char *' serves the purpose of producing warnings
+   for invalid uses of the value returned from this function.  */
 # if @REPLACE_SETLOCALE@
 #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
 #   undef setlocale
 #   define setlocale rpl_setlocale
 #   define GNULIB_defined_setlocale 1
 #  endif
-_GL_FUNCDECL_RPL (setlocale, char *, (int category, const char *locale), );
-_GL_CXXALIAS_RPL (setlocale, char *, (int category, const char *locale));
+_GL_FUNCDECL_RPL (setlocale, const char *,
+                  (int category, const char *locale), );
+_GL_CXXALIAS_RPL (setlocale, const char *,
+                  (int category, const char *locale));
 # else
-_GL_CXXALIAS_SYS (setlocale, char *, (int category, const char *locale));
+_GL_CXXALIAS_SYS_CAST (setlocale, const char *,
+                       (int category, const char *locale));
 # endif
 # if __GLIBC__ >= 2
 _GL_CXXALIASWARN (setlocale);
index d40e1e2efdc6e9b3ab0c7fd9e9ffa6fd1863f1cc..ee10a4425d6ff2ed9b48a92fe0d3b2e418f1b4c1 100644 (file)
@@ -73,11 +73,11 @@ extern void gl_locale_name_canonicalize (char *name);
 #  if NEED_SETLOCALE_IMPROVED
 static
 #  endif
-char *
+const char *
 setlocale_mtsafe (int category, const char *locale)
 {
   if (locale == NULL)
-    return (char *) setlocale_null (category);
+    return setlocale_null (category);
   else
     return setlocale (category, locale);
 }
@@ -668,7 +668,7 @@ search (const struct table_entry *table, size_t table_size, const char *string,
 
 /* Like setlocale, but accept also locale names in the form ll or ll_CC,
    where ll is an ISO 639 language code and CC is an ISO 3166 country code.  */
-static char *
+static const char *
 setlocale_unixlike (int category, const char *locale)
 {
   int is_utf8 = (GetACP () == 65001);
@@ -688,7 +688,7 @@ setlocale_unixlike (int category, const char *locale)
     locale = "English_United States.65001";
 
   /* First, try setlocale with the original argument unchanged.  */
-  char *result = setlocale_mtsafe (category, locale);
+  const char *result = setlocale_mtsafe (category, locale);
   if (result != NULL)
     return result;
 
@@ -845,10 +845,10 @@ setlocale_unixlike (int category, const char *locale)
 #  elif defined __ANDROID__
 
 /* Like setlocale, but accept also the locale names "C" and "POSIX".  */
-static char *
+static const char *
 setlocale_unixlike (int category, const char *locale)
 {
-  char *result = setlocale_fixed (category, locale);
+  const char *result = setlocale_fixed (category, locale);
   if (result == NULL)
     switch (category)
       {
@@ -866,7 +866,7 @@ setlocale_unixlike (int category, const char *locale)
       case LC_MEASUREMENT:
         if (locale == NULL
             || streq (locale, "C") || streq (locale, "POSIX"))
-          result = (char *) "C";
+          result = "C";
         break;
       default:
         break;
@@ -882,7 +882,7 @@ setlocale_unixlike (int category, const char *locale)
 #  if LC_MESSAGES == 1729
 
 /* Like setlocale, but support also LC_MESSAGES.  */
-static char *
+static const char *
 setlocale_single (int category, const char *locale)
 {
   if (category == LC_MESSAGES)
@@ -1417,7 +1417,7 @@ get_main_locale_with_same_territory (const char *locale)
 
 #  endif
 
-char *
+const char *
 setlocale_improved (int category, const char *locale)
 {
   if (locale != NULL && locale[0] == '\0')
@@ -1437,10 +1437,10 @@ setlocale_improved (int category, const char *locale)
             };
 
           /* Back up the old locale, in case one of the steps fails.  */
-          char *saved_locale = setlocale (LC_ALL, NULL);
-          if (saved_locale == NULL)
+          const char *old_locale = setlocale (LC_ALL, NULL);
+          if (old_locale == NULL)
             return NULL;
-          saved_locale = strdup (saved_locale);
+          char *saved_locale = strdup (old_locale);
           if (saved_locale == NULL)
             return NULL;
 
@@ -1672,18 +1672,16 @@ setlocale_improved (int category, const char *locale)
               /* In the underlying implementation, LC_ALL does not contain
                  LC_MESSAGES.  Therefore we need to handle LC_MESSAGES
                  separately.  */
-              char *result;
+              const char *result;
 
 #   if defined _WIN32 && ! defined __CYGWIN__
               if (strchr (native_locale, '.') != NULL)
                 {
-                  char *saved_locale;
-
                   /* Back up the old locale.  */
-                  saved_locale = setlocale (LC_ALL, NULL);
-                  if (saved_locale == NULL)
+                  const char *old_locale = setlocale (LC_ALL, NULL);
+                  if (old_locale == NULL)
                     return NULL;
-                  saved_locale = strdup (saved_locale);
+                  char *saved_locale = strdup (old_locale);
                   if (saved_locale == NULL)
                     return NULL;
 
@@ -1747,8 +1745,8 @@ setlocale_improved (int category, const char *locale)
      "LC_COLLATE=...;LC_CTYPE=...;LC_MONETARY=...;LC_NUMERIC=...;LC_TIME=..."
      If necessary, add ";LC_MESSAGES=..." at the end.  */
   {
-    char *name1 = setlocale (LC_ALL, NULL);
-    char *name2 = setlocale_single (LC_MESSAGES, NULL);
+    const char *name1 = setlocale (LC_ALL, NULL);
+    const char *name2 = setlocale_single (LC_MESSAGES, NULL);
     if (streq (name1, name2))
       /* Not a mixed locale.  */
       return name1;
index a14a7a093d426e6fdba39fb1eb734f5a995f3844..f8a8be460ac0da6715783eb4784a06af117e10d9 100644 (file)
@@ -1,5 +1,5 @@
 # mbrtoc16.m4
-# serial 4
+# serial 5
 dnl Copyright (C) 2014-2026 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -288,7 +288,7 @@ AC_DEFUN([gl_MBRTOC16_C_LOCALE],
             #include <uchar.h>
           ]], [[
             int i;
-            char *locale = setlocale (LC_ALL, "C");
+            const char *locale = setlocale (LC_ALL, "C");
             if (! locale)
               return 2;
             for (i = CHAR_MIN; i <= CHAR_MAX; i++)
index 3eaa49ff41218245e7c4ca31cae5c66f6dc9e009..9eb9a7e22320bb131b4eb820c2d68f02bce40757 100644 (file)
@@ -1,5 +1,5 @@
 # mbrtoc32.m4
-# serial 24
+# serial 25
 dnl Copyright (C) 2014-2026 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -179,7 +179,7 @@ AC_DEFUN([gl_MBRTOC32_C_LOCALE],
             #include <uchar.h>
           ]], [[
             int i;
-            char *locale = setlocale (LC_ALL, "C");
+            const char *locale = setlocale (LC_ALL, "C");
             if (! locale)
               return 2;
             for (i = CHAR_MIN; i <= CHAR_MAX; i++)
@@ -220,7 +220,7 @@ AC_DEFUN([gl_MBRTOC32_UTF8_LOCALE],
             #endif
             #include <uchar.h>
           ]], [[
-            char *locale = setlocale (LC_ALL, "en_US.UTF-8");
+            const char *locale = setlocale (LC_ALL, "en_US.UTF-8");
             if (locale)
               {
                 /* This test fails on Cygwin 3.5.3.  */
index fdc05da3a1e2c4a059dd3f507984466fd69374d1..456f5e214751933bc35617992339f44ffb9057c5 100644 (file)
@@ -1,5 +1,5 @@
 # mbrtowc.m4
-# serial 50
+# serial 51
 dnl Copyright (C) 2001-2002, 2004-2005, 2008-2026 Free Software Foundation,
 dnl Inc.
 dnl This file is free software; the Free Software Foundation
@@ -683,7 +683,7 @@ AC_DEFUN([gl_MBRTOWC_C_LOCALE],
             #include <wchar.h>
           ]], [[
             int i;
-            char *locale = setlocale (LC_ALL, "C");
+            const char *locale = setlocale (LC_ALL, "C");
             if (! locale)
               return 2;
             for (i = CHAR_MIN; i <= CHAR_MAX; i++)
index 80d9354eb14d1ba52aa98f6af93f76bc89374eec..25bd29d1af27a1b12cc93ca291f1a1c094340735 100644 (file)
@@ -29,7 +29,7 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::localeconv, struct lconv *, (void));
 #endif
 
 #if GNULIB_TEST_SETLOCALE
-SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, char *, (int, const char *));
+SIGNATURE_CHECK (GNULIB_NAMESPACE::setlocale, const char *, (int, const char *));
 #endif
 
 #if GNULIB_TEST_NEWLOCALE
index e78f4ece9cb9ec1b6df9249cabb52522365ca79a..b4fc1306105c22bff38b5f62d38af17ee3e83a47 100644 (file)
@@ -30,7 +30,7 @@ main (void)
 {
 #ifdef _UCRT
   /* Test that setlocale() works as expected in a UTF-8 locale.  */
-  char *name;
+  const char *name;
 
   /* This looks at all LC_*, LANG environment variables, which are all unset
      at this point.  */
index d622ea74d351e48d21012e65b8ee478218d25099..9bc56fe20050e1ecfd23b4a37942d8d1a9fd9558 100644 (file)
 #include <locale.h>
 
 #include "signature.h"
+#if GNULIB_defined_setlocale
+SIGNATURE_CHECK (setlocale, const char *, (int, const char *));
+#else
 SIGNATURE_CHECK (setlocale, char *, (int, const char *));
+#endif
 
 #include <stdlib.h>
 #include <string.h>