diff mbox series

more out of bounds checking improvements

Message ID 176ba75f-4299-073f-8319-66dbf9fe3f42@gmail.com
State Changes Requested
Headers show
Series more out of bounds checking improvements | expand

Commit Message

Martin Sebor Oct. 26, 2020, 3:01 p.m. UTC
The attached patch continues the improvements to out of bounds
checking(*) by decorating more APIs with either attribute access,
or by explicitly providing the array bound in APIs such as
tmpnam() that expect arrays of some minimum size as arguments.
(The latter feature is new in GCC 11.)

The only effects of the attribute and/or the array bound is to
check and diagnose calls to the functions that fail to provide
a sufficient number of elements, and the definitions of
the functions that access elements outside the specified bounds.
(There is no interplay with _FORTIFY_SOURCE here yet.)

For example, because the patch specifies the bound in the tmpnam
declaration like so:

    extern char *tmpnam (char[L_tmpnam]) __THROW __wur;

the following call to the function:

    char* warn_tmpnam (void)
    {
      char a[16];
      return tmpnam (a);
    }

triggers the warning below:

t.c: In function ‘warn_tmpnam’:
t.c:8:10: warning: ‘tmpnam’ accessing 20 bytes in a region of size 16 
[-Wstringop-overflow=]
      8 |   return tmpnam (a);
        |          ^~~~~~~~~~
t.c:8:10: note: referencing argument 1 of type ‘char *’
In file included from ../include/stdio.h:14,
                   from test-access-warn.c:1:
../libio/stdio.h:187:14: note: in a call to function ‘tmpnam’
    187 | extern char *tmpnam (char[L_tmpnam]) __THROW __wur;
        |              ^~~~~~

Unlike the [static N] notation, the plain array notation doesn't
require the argument to be nonnull.

Besides attribute access, the change adds attribute nonnull to
the readv and writev functions in misc/sys/uio.h.  The functions
don't necessarily access the array elements when their count is
zero but neither POSIX nor the Linux manual document this so it
seems appropriate to warn.

The patch introduces the _L_tmpnam macro to avoid polluting
the POSIX <unistd.h> namespace with L_tmpnam when the latter is
only supposed to be defined in <stdio.h>.  This in turn causes
the a number of POSIX conformance test failures that I haven't
been able to figure how to deal with and need some help with.

In file included from ../include/unistd.h:2,
                  from /tmp/tmpzm39v4n3/test.c:1:
../posix/unistd.h:1159:32: error: ‘_L_ctermid’ undeclared here (not in a 
function)
  extern char *ctermid (char __s[_L_ctermid]) __THROW
                                 ^~~~~~~~~~

I expected adding the new macros to stdio-common/stdio_lim.h.in
would do the trick but clearly something else is needed and I'm
at a lost as to what that might be.  I haven't been able to find
my way out of the maze of scripts and makefiles that tie all this
together.

Thanks
Martin

[*] https://sourceware.org/pipermail/libc-alpha/2020-April/113503.html

Comments

Florian Weimer Oct. 26, 2020, 3:41 p.m. UTC | #1
* Martin Sebor via Libc-alpha:

> Besides attribute access, the change adds attribute nonnull to
> the readv and writev functions in misc/sys/uio.h.  The functions
> don't necessarily access the array elements when their count is
> zero but neither POSIX nor the Linux manual document this so it
> seems appropriate to warn.

This change is questionable because it breaks interoperability with
abstract data types such as std::vector, where the base pointer can be
null when the array is empty.  The kernel does not return EFAULT in this
case, as expected.

I think we need a special case for this.

Thanks,
Florian
Joseph Myers Oct. 26, 2020, 4:08 p.m. UTC | #2
On Mon, 26 Oct 2020, Martin Sebor via Libc-alpha wrote:

> The patch introduces the _L_tmpnam macro to avoid polluting
> the POSIX <unistd.h> namespace with L_tmpnam when the latter is
> only supposed to be defined in <stdio.h>.  This in turn causes
> the a number of POSIX conformance test failures that I haven't
> been able to figure how to deal with and need some help with.
> 
> In file included from ../include/unistd.h:2,
>                  from /tmp/tmpzm39v4n3/test.c:1:
> ../posix/unistd.h:1159:32: error: ‘_L_ctermid’ undeclared here (not in a
> function)
>  extern char *ctermid (char __s[_L_ctermid]) __THROW
>                                 ^~~~~~~~~~
> 
> I expected adding the new macros to stdio-common/stdio_lim.h.in
> would do the trick but clearly something else is needed and I'm
> at a lost as to what that might be.  I haven't been able to find

<unistd.h> doesn't include <bits/stdio_lim.h>, and you're making 
<unistd.h> use _L_ctermid, and you're only defining _L_ctermid in 
<bits/stdio_lim.h>.  You need to define it in a header that <unistd.h> 
includes - which also needs to be one whose contents are namespace-clean 
for inclusion in <unistd.h> (which <bits/stdio_lim.h> isn't).

The obvious way would be to have a new installed (i.e. add to "headers" in 
the relevant Makefile) header for the new macros that can be included in 
both <stdio.h> and <unistd.h>.  Suggestion: the existing scheme for 
automatic generation of bits/stdio_lim.h is overly complicated, it would 
be better to use sysdeps headers in the normal way like for other bits/ 
headers where the values may depend on the glibc configuration (and then 
to have testcases that verify consistently of OPEN_MAX and FOPEN_MAX / of 
PATH_MAX and FILENAME_MAX, when both are defined).
diff mbox series

Patch

diff --git a/inet/if_index.c b/inet/if_index.c
index ddef419230..36f1806ac1 100644
--- a/inet/if_index.c
+++ b/inet/if_index.c
@@ -31,7 +31,7 @@  libc_hidden_weak (if_nametoindex)
 stub_warning (if_nametoindex)
 
 char *
-__if_indextoname (unsigned int ifindex, char *ifname)
+__if_indextoname (unsigned int ifindex, char ifname[IF_NAMESIZE])
 {
   __set_errno (ENOSYS);
   return NULL;
diff --git a/io/bits/poll2.h b/io/bits/poll2.h
index dca49717db..8cbedb7542 100644
--- a/io/bits/poll2.h
+++ b/io/bits/poll2.h
@@ -26,13 +26,14 @@  __BEGIN_DECLS
 extern int __REDIRECT (__poll_alias, (struct pollfd *__fds, nfds_t __nfds,
 				      int __timeout), poll);
 extern int __poll_chk (struct pollfd *__fds, nfds_t __nfds, int __timeout,
-		       __SIZE_TYPE__ __fdslen);
+		       __SIZE_TYPE__ __fdslen)
+    __attr_access ((__write_only__, 1, 2));
 extern int __REDIRECT (__poll_chk_warn, (struct pollfd *__fds, nfds_t __nfds,
 					 int __timeout, __SIZE_TYPE__ __fdslen),
 		       __poll_chk)
   __warnattr ("poll called with fds buffer too small file nfds entries");
 
-__fortify_function int
+__fortify_function __attr_access ((__write_only__, 1, 2)) int
 poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
 {
   if (__bos (__fds) != (__SIZE_TYPE__) -1)
@@ -53,7 +54,8 @@  extern int __REDIRECT (__ppoll_alias, (struct pollfd *__fds, nfds_t __nfds,
 				       const __sigset_t *__ss), ppoll);
 extern int __ppoll_chk (struct pollfd *__fds, nfds_t __nfds,
 			const struct timespec *__timeout,
-			const __sigset_t *__ss, __SIZE_TYPE__ __fdslen);
+			const __sigset_t *__ss, __SIZE_TYPE__ __fdslen)
+    __attr_access ((__write_only__, 1, 2));
 extern int __REDIRECT (__ppoll_chk_warn, (struct pollfd *__fds, nfds_t __nfds,
 					  const struct timespec *__timeout,
 					  const __sigset_t *__ss,
@@ -61,7 +63,7 @@  extern int __REDIRECT (__ppoll_chk_warn, (struct pollfd *__fds, nfds_t __nfds,
 		       __ppoll_chk)
   __warnattr ("ppoll called with fds buffer too small file nfds entries");
 
-__fortify_function int
+__fortify_function __attr_access ((__write_only__, 1, 2)) int
 ppoll (struct pollfd *__fds, nfds_t __nfds, const struct timespec *__timeout,
        const __sigset_t *__ss)
 {
diff --git a/io/sys/poll.h b/io/sys/poll.h
index 857be0f5ac..aa145a1737 100644
--- a/io/sys/poll.h
+++ b/io/sys/poll.h
@@ -51,7 +51,8 @@  __BEGIN_DECLS
 
    This function is a cancellation point and therefore not marked with
    __THROW.  */
-extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
+extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
+    __attr_access ((__write_only__, 1, 2));
 
 #ifdef __USE_GNU
 /* Like poll, but before waiting the threads signal mask is replaced
@@ -62,7 +63,9 @@  extern int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout);
    __THROW.  */
 extern int ppoll (struct pollfd *__fds, nfds_t __nfds,
 		  const struct timespec *__timeout,
-		  const __sigset_t *__ss);
+		  const __sigset_t *__ss)
+    __attr_access ((__write_only__, 1, 2));
+
 #endif
 
 __END_DECLS
diff --git a/libio/stdio.h b/libio/stdio.h
index 998470943e..59f2ee01ab 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -184,12 +184,12 @@  extern FILE *tmpfile64 (void) __wur;
 #endif
 
 /* Generate a temporary filename.  */
-extern char *tmpnam (char *__s) __THROW __wur;
+extern char *tmpnam (char[L_tmpnam]) __THROW __wur;
 
 #ifdef __USE_MISC
 /* This is the reentrant variant of `tmpnam'.  The only difference is
    that it does not allow S to be NULL.  */
-extern char *tmpnam_r (char *__s) __THROW __wur;
+extern char *tmpnam_r (char __s[L_tmpnam]) __THROW __wur;
 #endif
 
 
@@ -808,13 +808,13 @@  extern int pclose (FILE *__stream);
 
 #ifdef	__USE_POSIX
 /* Return the name of the controlling terminal.  */
-extern char *ctermid (char *__s) __THROW;
+extern char *ctermid (char __s[_L_ctermid]) __THROW;
 #endif /* Use POSIX.  */
 
 
 #if (defined __USE_XOPEN && !defined __USE_XOPEN2K) || defined __USE_GNU
 /* Return the name of the current user.  */
-extern char *cuserid (char *__s);
+extern char *cuserid (char __s[_L_cuserid]);
 #endif /* Use X/Open, but not issue 6.  */
 
 
diff --git a/misc/sys/uio.h b/misc/sys/uio.h
index 26d87c9f34..04ad46d705 100644
--- a/misc/sys/uio.h
+++ b/misc/sys/uio.h
@@ -39,7 +39,7 @@  __BEGIN_DECLS
    This function is a cancellation point and therefore not marked with
    __THROW.  */
 extern ssize_t readv (int __fd, const struct iovec *__iovec, int __count)
-  __wur;
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 
 /* Write data pointed by the buffers described by IOVEC, which
    is a vector of COUNT 'struct iovec's, to file descriptor FD.
@@ -50,7 +50,7 @@  extern ssize_t readv (int __fd, const struct iovec *__iovec, int __count)
    This function is a cancellation point and therefore not marked with
    __THROW.  */
 extern ssize_t writev (int __fd, const struct iovec *__iovec, int __count)
-  __wur;
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 
 
 #ifdef __USE_MISC
@@ -65,7 +65,8 @@  extern ssize_t writev (int __fd, const struct iovec *__iovec, int __count)
    This function is a cancellation point and therefore not marked with
    __THROW.  */
 extern ssize_t preadv (int __fd, const struct iovec *__iovec, int __count,
-		       __off_t __offset) __wur;
+		       __off_t __offset)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 
 /* Write data pointed by the buffers described by IOVEC, which is a
    vector of COUNT 'struct iovec's, to file descriptor FD at the given
@@ -77,16 +78,19 @@  extern ssize_t preadv (int __fd, const struct iovec *__iovec, int __count,
    This function is a cancellation point and therefore not marked with
    __THROW.  */
 extern ssize_t pwritev (int __fd, const struct iovec *__iovec, int __count,
-			__off_t __offset) __wur;
+			__off_t __offset)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 
 # else
 #  ifdef __REDIRECT
 extern ssize_t __REDIRECT (preadv, (int __fd, const struct iovec *__iovec,
 				    int __count, __off64_t __offset),
-			   preadv64) __wur;
+			   preadv64)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 extern ssize_t __REDIRECT (pwritev, (int __fd, const struct iovec *__iovec,
 				     int __count, __off64_t __offset),
-			   pwritev64) __wur;
+			   pwritev64)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 #  else
 #   define preadv preadv64
 #   define pwritev pwritev64
@@ -104,7 +108,8 @@  extern ssize_t __REDIRECT (pwritev, (int __fd, const struct iovec *__iovec,
    This function is a cancellation point and therefore not marked with
    __THROW.  */
 extern ssize_t preadv64 (int __fd, const struct iovec *__iovec, int __count,
-			 __off64_t __offset) __wur;
+			 __off64_t __offset)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 
 /* Write data pointed by the buffers described by IOVEC, which is a
    vector of COUNT 'struct iovec's, to file descriptor FD at the given
@@ -116,7 +121,8 @@  extern ssize_t preadv64 (int __fd, const struct iovec *__iovec, int __count,
    This function is a cancellation point and therefore not marked with
    __THROW.  */
 extern ssize_t pwritev64 (int __fd, const struct iovec *__iovec, int __count,
-			  __off64_t __offset) __wur;
+			  __off64_t __offset)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 # endif
 #endif	/* Use misc.  */
 
@@ -125,7 +131,8 @@  extern ssize_t pwritev64 (int __fd, const struct iovec *__iovec, int __count,
 # ifndef __USE_FILE_OFFSET64
 /* Same as preadv but with an additional flag argumenti defined at uio.h.  */
 extern ssize_t preadv2 (int __fp, const struct iovec *__iovec, int __count,
-			__off_t __offset, int ___flags) __wur;
+			__off_t __offset, int ___flags)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 
 /* Same as preadv but with an additional flag argument defined at uio.h.  */
 extern ssize_t pwritev2 (int __fd, const struct iovec *__iodev, int __count,
@@ -136,11 +143,13 @@  extern ssize_t pwritev2 (int __fd, const struct iovec *__iodev, int __count,
 extern ssize_t __REDIRECT (pwritev2, (int __fd, const struct iovec *__iovec,
 				      int __count, __off64_t __offset,
 				      int __flags),
-			   pwritev64v2) __wur;
+			   pwritev64v2)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 extern ssize_t __REDIRECT (preadv2, (int __fd, const struct iovec *__iovec,
 				     int __count, __off64_t __offset,
 				     int __flags),
-			   preadv64v2) __wur;
+			   preadv64v2)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 #  else
 #   define preadv2 preadv64v2
 #   define pwritev2 pwritev64v2
@@ -151,12 +160,14 @@  extern ssize_t __REDIRECT (preadv2, (int __fd, const struct iovec *__iovec,
 /* Same as preadv but with an additional flag argumenti defined at uio.h.  */
 extern ssize_t preadv64v2 (int __fp, const struct iovec *__iovec,
 			   int __count, __off64_t __offset,
-			   int ___flags) __wur;
+			   int ___flags)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 
 /* Same as preadv but with an additional flag argument defined at uio.h.  */
 extern ssize_t pwritev64v2 (int __fd, const struct iovec *__iodev,
 			    int __count, __off64_t __offset,
-			    int __flags) __wur;
+			    int __flags)
+  __wur __attr_access ((__read_only__, 2, 3)) __nonnull ((2));
 # endif
 #endif /* Use GNU.  */
 
diff --git a/posix/regex.h b/posix/regex.h
index 5fe41c8685..75c9201fc6 100644
--- a/posix/regex.h
+++ b/posix/regex.h
@@ -536,7 +536,8 @@  extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
    'regcomp', with a malloc'ed value, or set to NULL before calling
    'regfree'.  */
 extern const char *re_compile_pattern (const char *__pattern, size_t __length,
-				       struct re_pattern_buffer *__buffer);
+				       struct re_pattern_buffer *__buffer)
+    __attr_access ((__read_only__, 1, 2));
 
 
 /* Compile a fastmap for the compiled pattern in BUFFER; used to
@@ -553,7 +554,8 @@  extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
 extern regoff_t re_search (struct re_pattern_buffer *__buffer,
 			   const char *__String, regoff_t __length,
 			   regoff_t __start, regoff_t __range,
-			   struct re_registers *__regs);
+			   struct re_registers *__regs)
+    __attr_access ((__read_only__, 2, 3));
 
 
 /* Like 're_search', but search in the concatenation of STRING1 and
@@ -563,14 +565,17 @@  extern regoff_t re_search_2 (struct re_pattern_buffer *__buffer,
 			     const char *__string2, regoff_t __length2,
 			     regoff_t __start, regoff_t __range,
 			     struct re_registers *__regs,
-			     regoff_t __stop);
+			     regoff_t __stop)
+    __attr_access ((__read_only__, 2, 3))
+    __attr_access ((__read_only__, 4, 5));
 
 
 /* Like 're_search', but return how many characters in STRING the regexp
    in BUFFER matched, starting at position START.  */
 extern regoff_t re_match (struct re_pattern_buffer *__buffer,
 			  const char *__String, regoff_t __length,
-			  regoff_t __start, struct re_registers *__regs);
+			  regoff_t __start, struct re_registers *__regs)
+    __attr_access ((__read_only__, 2, 3));
 
 
 /* Relates to 're_match' as 're_search_2' relates to 're_search'.  */
@@ -578,7 +583,9 @@  extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer,
 			    const char *__string1, regoff_t __length1,
 			    const char *__string2, regoff_t __length2,
 			    regoff_t __start, struct re_registers *__regs,
-			    regoff_t __stop);
+			    regoff_t __stop)
+    __attr_access ((__read_only__, 2, 3))
+    __attr_access ((__read_only__, 4, 5));
 
 
 /* Set REGS to hold NUM_REGS registers, storing them in STARTS and
@@ -641,10 +648,12 @@  extern int regcomp (regex_t *_Restrict_ __preg,
 extern int regexec (const regex_t *_Restrict_ __preg,
 		    const char *_Restrict_ __String, size_t __nmatch,
 		    regmatch_t __pmatch[_Restrict_arr_],
-		    int __eflags);
+		    int __eflags)
+    __attr_access ((__write_only__, 4, 3));
 
 extern size_t regerror (int __errcode, const regex_t *_Restrict_ __preg,
-			char *_Restrict_ __errbuf, size_t __errbuf_size);
+			char *_Restrict_ __errbuf, size_t __errbuf_size)
+    __attr_access ((__write_only__, 3, 4));
 
 extern void regfree (regex_t *__preg);
 
diff --git a/posix/unistd.h b/posix/unistd.h
index 32b8161619..0eb79fb6ce 100644
--- a/posix/unistd.h
+++ b/posix/unistd.h
@@ -1156,10 +1156,12 @@  extern void swab (const void *__restrict __from, void *__restrict __to,
    <stdio.h>.  */
 #if defined __USE_XOPEN && !defined __USE_XOPEN2K
 /* Return the name of the controlling terminal.  */
-extern char *ctermid (char *__s) __THROW;
+extern char *ctermid (char __s[_L_ctermid]) __THROW
+    __attr_access ((__write_only__, 1));
 
 /* Return the name of the current user.  */
-extern char *cuserid (char *__s);
+extern char *cuserid (char __s[_L_cuserid])
+    __attr_access ((__write_only__, 1))
 #endif
 
 
diff --git a/pwd/pwd.h b/pwd/pwd.h
index bbc29479cd..065a19bade 100644
--- a/pwd/pwd.h
+++ b/pwd/pwd.h
@@ -139,20 +139,23 @@  extern struct passwd *getpwnam (const char *__name) __nonnull ((1));
 extern int getpwent_r (struct passwd *__restrict __resultbuf,
 		       char *__restrict __buffer, size_t __buflen,
 		       struct passwd **__restrict __result)
-		       __nonnull ((1, 2, 4));
+    __nonnull ((1, 2, 4))
+    __attr_access ((__write_only__, 2, 3));
 # endif
 
 extern int getpwuid_r (__uid_t __uid,
 		       struct passwd *__restrict __resultbuf,
 		       char *__restrict __buffer, size_t __buflen,
 		       struct passwd **__restrict __result)
-		       __nonnull ((2, 3, 5));
+    __nonnull ((2, 3, 5))
+    __attr_access ((__write_only__, 3, 4));
 
 extern int getpwnam_r (const char *__restrict __name,
 		       struct passwd *__restrict __resultbuf,
 		       char *__restrict __buffer, size_t __buflen,
 		       struct passwd **__restrict __result)
-		       __nonnull ((1, 2, 3, 5));
+    __nonnull ((1, 2, 3, 5))
+    __attr_access ((__write_only__, 3, 4));
 
 
 # ifdef	__USE_MISC
@@ -167,7 +170,8 @@  extern int fgetpwent_r (FILE *__restrict __stream,
 			struct passwd *__restrict __resultbuf,
 			char *__restrict __buffer, size_t __buflen,
 			struct passwd **__restrict __result)
-			__nonnull ((1, 2, 3, 5));
+    __nonnull ((1, 2, 3, 5))
+    __attr_access ((__write_only__, 3, 4));
 # endif
 
 #endif	/* POSIX or reentrant */
diff --git a/stdio-common/cuserid.c b/stdio-common/cuserid.c
index d4f1861c99..578887e82b 100644
--- a/stdio-common/cuserid.c
+++ b/stdio-common/cuserid.c
@@ -22,7 +22,7 @@ 
    If S is not NULL, it points to a buffer of at least L_cuserid bytes
    into which the name is copied; otherwise, a static buffer is used.  */
 char *
-cuserid (char *s)
+cuserid (char s[L_cuserid])
 {
   __set_errno (ENOSYS);
   return NULL;
diff --git a/stdio-common/stdio_lim.h.in b/stdio-common/stdio_lim.h.in
index de0caa5fee..9e3c59c942 100644
--- a/stdio-common/stdio_lim.h.in
+++ b/stdio-common/stdio_lim.h.in
@@ -26,6 +26,9 @@ 
 #define TMP_MAX @TMP_MAX@
 #define FILENAME_MAX @FILENAME_MAX@
 
+#define _L_ctermid @L_ctermid@
+#define _L_cuserid @L_cuserid@
+
 #ifdef __USE_POSIX
 # define L_ctermid @L_ctermid@
 # if !defined __USE_XOPEN2K || defined __USE_GNU
diff --git a/stdio-common/tmpnam.c b/stdio-common/tmpnam.c
index a8e0ca5b3b..cdd64e1d54 100644
--- a/stdio-common/tmpnam.c
+++ b/stdio-common/tmpnam.c
@@ -24,7 +24,7 @@  static char tmpnam_buffer[L_tmpnam];
 
    This function is *not* thread safe!  */
 char *
-tmpnam (char *s)
+tmpnam (char s[L_tmpnam])
 {
   /* By using two buffers we manage to be thread safe in the case
      where S != NULL.  */
diff --git a/stdio-common/tmpnam_r.c b/stdio-common/tmpnam_r.c
index 49f762f392..e359e4808a 100644
--- a/stdio-common/tmpnam_r.c
+++ b/stdio-common/tmpnam_r.c
@@ -20,7 +20,7 @@ 
 /* Generate a unique filename in P_tmpdir.  If S is NULL return NULL.
    This makes this function thread safe.  */
 char *
-tmpnam_r (char *s)
+tmpnam_r (char s[L_tmpnam])
 {
   if (s == NULL)
     return NULL;
diff --git a/stdlib/monetary.h b/stdlib/monetary.h
index c9d3c64e14..37ee8ab6d2 100644
--- a/stdlib/monetary.h
+++ b/stdlib/monetary.h
@@ -37,7 +37,8 @@  __BEGIN_DECLS
 /* Formatting a monetary value according to the current locale.  */
 extern ssize_t strfmon (char *__restrict __s, size_t __maxsize,
 			const char *__restrict __format, ...)
-     __THROW __attribute_format_strfmon__ (3, 4);
+     __THROW __attribute_format_strfmon__ (3, 4)
+     __attr_access ((__write_only__, 1, 2));
 
 #ifdef __USE_XOPEN2K8
 /* POSIX.1-2008 extended locale interface (see locale.h).  */
@@ -47,7 +48,8 @@  extern ssize_t strfmon (char *__restrict __s, size_t __maxsize,
 extern ssize_t strfmon_l (char *__restrict __s, size_t __maxsize,
 			  locale_t __loc,
 			  const char *__restrict __format, ...)
-     __THROW __attribute_format_strfmon__ (4, 5);
+     __THROW __attribute_format_strfmon__ (4, 5)
+     __attr_access ((__write_only__, 1, 2));
 #endif
 
 #include <bits/floatn.h>
diff --git a/sysdeps/gnu/net/if.h b/sysdeps/gnu/net/if.h
index e587a5ce59..30f3e281e5 100644
--- a/sysdeps/gnu/net/if.h
+++ b/sysdeps/gnu/net/if.h
@@ -191,7 +191,9 @@  __BEGIN_DECLS
 
 /* Convert an interface name to an index, and vice versa.  */
 extern unsigned int if_nametoindex (const char *__ifname) __THROW;
-extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW;
+extern char *if_indextoname (unsigned int __ifindex,
+			     char __ifname[IF_NAMESIZE]) __THROW
+    __attr_access ((__write_only__, 2));
 
 /* Return a list of all interfaces and their indices.  */
 extern struct if_nameindex *if_nameindex (void) __THROW;
diff --git a/sysdeps/mach/hurd/if_index.c b/sysdeps/mach/hurd/if_index.c
index 32dceccdbf..f92cd5723a 100644
--- a/sysdeps/mach/hurd/if_index.c
+++ b/sysdeps/mach/hurd/if_index.c
@@ -166,7 +166,7 @@  libc_hidden_weak (if_nameindex)
    IFNAME (which has space for at least IFNAMSIZ characters).  Return
    IFNAME, or NULL on error.  */
 char *
-__if_indextoname (unsigned int ifindex, char *ifname)
+__if_indextoname (unsigned int ifindex, char ifname[IF_NAMESIZE])
 {
   struct ifreq ifr;
   int fd = __opensock ();
diff --git a/sysdeps/posix/ctermid.c b/sysdeps/posix/ctermid.c
index 49f3f08a6f..4112cb0f2f 100644
--- a/sysdeps/posix/ctermid.c
+++ b/sysdeps/posix/ctermid.c
@@ -24,7 +24,7 @@ 
    long), otherwise we return a pointer to a non-const but read-only
    string literal, that POSIX states the caller must not modify.  */
 char *
-ctermid (char *s)
+ctermid (char s[L_ctermid])
 {
   char *name = (char /*drop const*/ *) "/dev/tty";
 
diff --git a/sysdeps/posix/cuserid.c b/sysdeps/posix/cuserid.c
index 401b100333..213802f0f9 100644
--- a/sysdeps/posix/cuserid.c
+++ b/sysdeps/posix/cuserid.c
@@ -25,7 +25,7 @@ 
    If S is not NULL, it points to a buffer of at least L_cuserid bytes
    into which the name is copied; otherwise, a static buffer is used.  */
 char *
-cuserid (char *s)
+cuserid (char s[L_cuserid])
 {
   static char name[L_cuserid];
   char buf[NSS_BUFLEN_PASSWD];
diff --git a/sysdeps/unix/sysv/linux/if_index.c b/sysdeps/unix/sysv/linux/if_index.c
index dffa363931..5509da2c21 100644
--- a/sysdeps/unix/sysv/linux/if_index.c
+++ b/sysdeps/unix/sysv/linux/if_index.c
@@ -215,7 +215,7 @@  libc_hidden_weak (if_nameindex)
 
 
 char *
-__if_indextoname (unsigned int ifindex, char *ifname)
+__if_indextoname (unsigned int ifindex, char ifname[IF_NAMESIZE])
 {
   /* We may be able to do the conversion directly, rather than searching a
      list.  This ioctl is not present in kernels before version 2.1.50.  */