Re: [PATCH v2 2/3] Mark functions with the __nocapture attribute

From: Kees Cook
Date: Tue Jul 12 2016 - 15:09:42 EST


On Mon, Jul 4, 2016 at 7:42 PM, Emese Revfy <re.emese@xxxxxxxxx> wrote:
> The nocapture gcc attribute can be on functions only.
> The attribute takes one or more unsigned integer constants as parameters
> that specify the function argument(s) of const char* type to initify.
> If the marked argument is a vararg then the plugin initifies
> all vararg arguments.

Why is this called "nocapture"? Not captured by what? It seems like it
means "initify this if possible". Am I misunderstanding its purpose?

-Kees

> I couldn't test the arm, arm64 and powerpc architectures.

Has anyone had a chance to try this out on these architectures? I'll
be testing arm shortly...

-Kees

>
> Signed-off-by: Emese Revfy <re.emese@xxxxxxxxx>
> ---
> arch/arm/include/asm/string.h | 4 +--
> arch/arm64/include/asm/string.h | 19 ++++++------
> arch/powerpc/include/asm/string.h | 19 ++++++------
> arch/x86/include/asm/string_32.h | 21 ++++++-------
> arch/x86/include/asm/string_64.h | 20 ++++++-------
> arch/x86/kernel/hpet.c | 2 +-
> include/asm-generic/bug.h | 6 ++--
> include/linux/compiler-gcc.h | 10 +++++--
> include/linux/compiler.h | 4 +++
> include/linux/fs.h | 5 ++--
> include/linux/printk.h | 2 +-
> include/linux/string.h | 63 ++++++++++++++++++++-------------------
> 12 files changed, 96 insertions(+), 79 deletions(-)
>
> diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
> index cf4f3aa..ddb9d58 100644
> --- a/arch/arm/include/asm/string.h
> +++ b/arch/arm/include/asm/string.h
> @@ -13,10 +13,10 @@ extern char * strrchr(const char * s, int c);
> extern char * strchr(const char * s, int c);
>
> #define __HAVE_ARCH_MEMCPY
> -extern void * memcpy(void *, const void *, __kernel_size_t);
> +extern void * memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
>
> #define __HAVE_ARCH_MEMMOVE
> -extern void * memmove(void *, const void *, __kernel_size_t);
> +extern void * memmove(void *, const void *, __kernel_size_t) __nocapture(2);
>
> #define __HAVE_ARCH_MEMCHR
> extern void * memchr(const void *, int, __kernel_size_t);
> diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h
> index 2eb714c..4263a27 100644
> --- a/arch/arm64/include/asm/string.h
> +++ b/arch/arm64/include/asm/string.h
> @@ -23,24 +23,25 @@ extern char *strrchr(const char *, int c);
> extern char *strchr(const char *, int c);
>
> #define __HAVE_ARCH_STRCMP
> -extern int strcmp(const char *, const char *);
> +extern int strcmp(const char *, const char *) __nocapture(1, 2);
>
> #define __HAVE_ARCH_STRNCMP
> -extern int strncmp(const char *, const char *, __kernel_size_t);
> +extern int
> +strncmp(const char *, const char *, __kernel_size_t) __nocapture(1, 2);
>
> #define __HAVE_ARCH_STRLEN
> -extern __kernel_size_t strlen(const char *);
> +extern __kernel_size_t strlen(const char *) __nocapture(1);
>
> #define __HAVE_ARCH_STRNLEN
> -extern __kernel_size_t strnlen(const char *, __kernel_size_t);
> +extern __kernel_size_t strnlen(const char *, __kernel_size_t) __nocapture(1);
>
> #define __HAVE_ARCH_MEMCPY
> -extern void *memcpy(void *, const void *, __kernel_size_t);
> -extern void *__memcpy(void *, const void *, __kernel_size_t);
> +extern void *memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
> +extern void *__memcpy(void *, const void *, __kernel_size_t) __nocapture(2);
>
> #define __HAVE_ARCH_MEMMOVE
> -extern void *memmove(void *, const void *, __kernel_size_t);
> -extern void *__memmove(void *, const void *, __kernel_size_t);
> +extern void *memmove(void *, const void *, __kernel_size_t) __nocapture(2);
> +extern void *__memmove(void *, const void *, __kernel_size_t) __nocapture(2);
>
> #define __HAVE_ARCH_MEMCHR
> extern void *memchr(const void *, int, __kernel_size_t);
> @@ -50,7 +51,7 @@ extern void *memset(void *, int, __kernel_size_t);
> extern void *__memset(void *, int, __kernel_size_t);
>
> #define __HAVE_ARCH_MEMCMP
> -extern int memcmp(const void *, const void *, size_t);
> +extern int memcmp(const void *, const void *, size_t) __nocapture(1, 2);
>
>
> #if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
> diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h
> index da3cdff..e67f1eb 100644
> --- a/arch/powerpc/include/asm/string.h
> +++ b/arch/powerpc/include/asm/string.h
> @@ -11,16 +11,17 @@
> #define __HAVE_ARCH_MEMCMP
> #define __HAVE_ARCH_MEMCHR
>
> -extern char * strcpy(char *,const char *);
> -extern char * strncpy(char *,const char *, __kernel_size_t);
> -extern __kernel_size_t strlen(const char *);
> -extern int strcmp(const char *,const char *);
> -extern int strncmp(const char *, const char *, __kernel_size_t);
> -extern char * strcat(char *, const char *);
> +extern char * strcpy(char *,const char *) __nocapture(2);
> +extern char * strncpy(char *,const char *, __kernel_size_t) __nocapture(2);
> +extern __kernel_size_t strlen(const char *) __nocapture(1);
> +extern int strcmp(const char *,const char *) __nocapture(1, 2);
> +extern int
> +strncmp(const char *, const char *, __kernel_size_t) __nocapture(1, 2);
> +extern char * strcat(char *, const char *) __nocapture(2);
> extern void * memset(void *,int,__kernel_size_t);
> -extern void * memcpy(void *,const void *,__kernel_size_t);
> -extern void * memmove(void *,const void *,__kernel_size_t);
> -extern int memcmp(const void *,const void *,__kernel_size_t);
> +extern void * memcpy(void *,const void *,__kernel_size_t) __nocapture(2);
> +extern void * memmove(void *,const void *,__kernel_size_t) __nocapture(2);
> +extern int memcmp(const void *,const void *,__kernel_size_t) __nocapture(1, 2);
> extern void * memchr(const void *,int,__kernel_size_t);
>
> #endif /* __KERNEL__ */
> diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h
> index 3d3e835..63f29bf 100644
> --- a/arch/x86/include/asm/string_32.h
> +++ b/arch/x86/include/asm/string_32.h
> @@ -6,28 +6,29 @@
> /* Let gcc decide whether to inline or use the out of line functions */
>
> #define __HAVE_ARCH_STRCPY
> -extern char *strcpy(char *dest, const char *src);
> +extern char *strcpy(char *dest, const char *src) __nocapture(2);
>
> #define __HAVE_ARCH_STRNCPY
> -extern char *strncpy(char *dest, const char *src, size_t count);
> +extern char *strncpy(char *dest, const char *src, size_t count) __nocapture(2);
>
> #define __HAVE_ARCH_STRCAT
> -extern char *strcat(char *dest, const char *src);
> +extern char *strcat(char *dest, const char *src) __nocapture(2);
>
> #define __HAVE_ARCH_STRNCAT
> -extern char *strncat(char *dest, const char *src, size_t count);
> +extern char *strncat(char *dest, const char *src, size_t count) __nocapture(2);
>
> #define __HAVE_ARCH_STRCMP
> -extern int strcmp(const char *cs, const char *ct);
> +extern int strcmp(const char *cs, const char *ct) __nocapture(1, 2);
>
> #define __HAVE_ARCH_STRNCMP
> -extern int strncmp(const char *cs, const char *ct, size_t count);
> +extern int
> +strncmp(const char *cs, const char *ct, size_t count) __nocapture(1, 2);
>
> #define __HAVE_ARCH_STRCHR
> extern char *strchr(const char *s, int c);
>
> #define __HAVE_ARCH_STRLEN
> -extern size_t strlen(const char *s);
> +extern size_t strlen(const char *s) __nocapture(1);
>
> static __always_inline void *__memcpy(void *to, const void *from, size_t n)
> {
> @@ -197,7 +198,7 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len)
> #endif
>
> #define __HAVE_ARCH_MEMMOVE
> -void *memmove(void *dest, const void *src, size_t n);
> +void *memmove(void *dest, const void *src, size_t n) __nocapture(2);
>
> #define memcmp __builtin_memcmp
>
> @@ -243,11 +244,11 @@ void *__constant_c_memset(void *s, unsigned long c, size_t count)
>
> /* Added by Gertjan van Wingerde to make minix and sysv module work */
> #define __HAVE_ARCH_STRNLEN
> -extern size_t strnlen(const char *s, size_t count);
> +extern size_t strnlen(const char *s, size_t count) __nocapture(1);
> /* end of additional stuff */
>
> #define __HAVE_ARCH_STRSTR
> -extern char *strstr(const char *cs, const char *ct);
> +extern char *strstr(const char *cs, const char *ct) __nocapture(2);
>
> /*
> * This looks horribly ugly, but the compiler can optimize it totally,
> diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
> index 90dbbd9..607d3ba 100644
> --- a/arch/x86/include/asm/string_64.h
> +++ b/arch/x86/include/asm/string_64.h
> @@ -27,8 +27,8 @@ static __always_inline void *__inline_memcpy(void *to, const void *from, size_t
> function. */
>
> #define __HAVE_ARCH_MEMCPY 1
> -extern void *memcpy(void *to, const void *from, size_t len);
> -extern void *__memcpy(void *to, const void *from, size_t len);
> +extern void *memcpy(void *to, const void *from, size_t len) __nocapture(2);
> +extern void *__memcpy(void *to, const void *from, size_t len) __nocapture(2);
>
> #ifndef CONFIG_KMEMCHECK
> #if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4
> @@ -56,14 +56,14 @@ void *memset(void *s, int c, size_t n);
> void *__memset(void *s, int c, size_t n);
>
> #define __HAVE_ARCH_MEMMOVE
> -void *memmove(void *dest, const void *src, size_t count);
> -void *__memmove(void *dest, const void *src, size_t count);
> +void *memmove(void *dest, const void *src, size_t count) __nocapture(2);
> +void *__memmove(void *dest, const void *src, size_t count) __nocapture(2);
>
> -int memcmp(const void *cs, const void *ct, size_t count);
> -size_t strlen(const char *s);
> -char *strcpy(char *dest, const char *src);
> -char *strcat(char *dest, const char *src);
> -int strcmp(const char *cs, const char *ct);
> +int memcmp(const void *cs, const void *ct, size_t count) __nocapture(1, 2);
> +size_t strlen(const char *s) __nocapture(1);
> +char *strcpy(char *dest, const char *src) __nocapture(2);
> +char *strcat(char *dest, const char *src) __nocapture(2);
> +int strcmp(const char *cs, const char *ct) __nocapture(1, 2);
>
> #if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
>
> @@ -89,7 +89,7 @@ int strcmp(const char *cs, const char *ct);
> *
> * Return 0 for success, -EFAULT for fail
> */
> -int memcpy_mcsafe(void *dst, const void *src, size_t cnt);
> +int memcpy_mcsafe(void *dst, const void *src, size_t cnt) __nocapture(2);
>
> #endif /* __KERNEL__ */
>
> diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
> index 357900a..c68ab1a 100644
> --- a/arch/x86/kernel/hpet.c
> +++ b/arch/x86/kernel/hpet.c
> @@ -136,7 +136,7 @@ int is_hpet_enabled(void)
> }
> EXPORT_SYMBOL_GPL(is_hpet_enabled);
>
> -static void _hpet_print_config(const char *function, int line)
> +static void __nocapture(1) _hpet_print_config(const char *function, int line)
> {
> u32 i, timers, l, h;
> printk(KERN_INFO "hpet: %s(%d):\n", function, line);
> diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
> index 6f96247..630d0c4 100644
> --- a/include/asm-generic/bug.h
> +++ b/include/asm-generic/bug.h
> @@ -62,13 +62,13 @@ struct bug_entry {
> * to provide better diagnostics.
> */
> #ifndef __WARN_TAINT
> -extern __printf(3, 4)
> +extern __printf(3, 4) __nocapture(1)
> void warn_slowpath_fmt(const char *file, const int line,
> const char *fmt, ...);
> -extern __printf(4, 5)
> +extern __printf(4, 5) __nocapture(1)
> void warn_slowpath_fmt_taint(const char *file, const int line, unsigned taint,
> const char *fmt, ...);
> -extern void warn_slowpath_null(const char *file, const int line);
> +extern __nocapture(1) void warn_slowpath_null(const char *file, const int line);
> #define WANT_WARN_ON_SLOWPATH
> #define __WARN() warn_slowpath_null(__FILE__, __LINE__)
> #define __WARN_printf(arg...) warn_slowpath_fmt(__FILE__, __LINE__, arg)
> diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
> index df88c0a..192cea4 100644
> --- a/include/linux/compiler-gcc.h
> +++ b/include/linux/compiler-gcc.h
> @@ -116,8 +116,10 @@
> */
> #define __pure __attribute__((pure))
> #define __aligned(x) __attribute__((aligned(x)))
> -#define __printf(a, b) __attribute__((format(printf, a, b)))
> -#define __scanf(a, b) __attribute__((format(scanf, a, b)))
> +#define __printf(a, b) __attribute__((format(printf, a, b))) \
> + __nocapture(a, b)
> +#define __scanf(a, b) __attribute__((format(scanf, a, b))) \
> + __nocapture(a, b)
> #define __attribute_const__ __attribute__((__const__))
> #define __maybe_unused __attribute__((unused))
> #define __always_unused __attribute__((unused))
> @@ -193,6 +195,10 @@
> # define __latent_entropy __attribute__((latent_entropy))
> #endif
>
> +#ifdef INITIFY_PLUGIN
> +#define __nocapture(...) __attribute__((nocapture(__VA_ARGS__)))
> +#endif
> +
> /*
> * Mark a position in code as unreachable. This can be used to
> * suppress control flow warnings after asm blocks that transfer
> diff --git a/include/linux/compiler.h b/include/linux/compiler.h
> index 0adcfc2..391b48b 100644
> --- a/include/linux/compiler.h
> +++ b/include/linux/compiler.h
> @@ -412,6 +412,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
> # define __latent_entropy
> #endif
>
> +#ifndef __nocapture
> +# define __nocapture(...)
> +#endif
> +
> /*
> * Tell gcc if a function is cold. The compiler will assume any path
> * directly leading to the call is unlikely.
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index c4ab2cf..048a0d9 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2476,8 +2476,9 @@ extern int register_chrdev_region(dev_t, unsigned, const char *);
> extern int __register_chrdev(unsigned int major, unsigned int baseminor,
> unsigned int count, const char *name,
> const struct file_operations *fops);
> -extern void __unregister_chrdev(unsigned int major, unsigned int baseminor,
> - unsigned int count, const char *name);
> +extern __nocapture(4) void __unregister_chrdev(unsigned int major,
> + unsigned int baseminor, unsigned int count,
> + const char *name);
> extern void unregister_chrdev_region(dev_t, unsigned);
> extern void chrdev_show(struct seq_file *,off_t);
>
> diff --git a/include/linux/printk.h b/include/linux/printk.h
> index e6ff22e..ca137fa 100644
> --- a/include/linux/printk.h
> +++ b/include/linux/printk.h
> @@ -163,7 +163,7 @@ __printf(1, 2) __cold int printk_deferred(const char *fmt, ...);
> * with all other unrelated printk_ratelimit() callsites. Instead use
> * printk_ratelimited() or plain old __ratelimit().
> */
> -extern int __printk_ratelimit(const char *func);
> +extern int __printk_ratelimit(const char *func) __nocapture(1);
> #define printk_ratelimit() __printk_ratelimit(__func__)
> extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
> unsigned int interval_msec);
> diff --git a/include/linux/string.h b/include/linux/string.h
> index 26b6f6a..0a56912 100644
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -18,37 +18,38 @@ extern void *memdup_user_nul(const void __user *, size_t);
> #include <asm/string.h>
>
> #ifndef __HAVE_ARCH_STRCPY
> -extern char * strcpy(char *,const char *);
> +extern char * strcpy(char *,const char *) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRNCPY
> -extern char * strncpy(char *,const char *, __kernel_size_t);
> +extern char * strncpy(char *,const char *, __kernel_size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRLCPY
> -size_t strlcpy(char *, const char *, size_t);
> +size_t strlcpy(char *, const char *, size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRSCPY
> -ssize_t __must_check strscpy(char *, const char *, size_t);
> +ssize_t __must_check strscpy(char *, const char *, size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRCAT
> -extern char * strcat(char *, const char *);
> +extern char * strcat(char *, const char *) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRNCAT
> -extern char * strncat(char *, const char *, __kernel_size_t);
> +extern char * strncat(char *, const char *, __kernel_size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRLCAT
> -extern size_t strlcat(char *, const char *, __kernel_size_t);
> +extern size_t strlcat(char *, const char *, __kernel_size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRCMP
> -extern int strcmp(const char *,const char *);
> +extern int strcmp(const char *,const char *) __nocapture(1, 2);
> #endif
> #ifndef __HAVE_ARCH_STRNCMP
> -extern int strncmp(const char *,const char *,__kernel_size_t);
> +extern int strncmp(const char *,const char *,__kernel_size_t) __nocapture(1, 2);
> #endif
> #ifndef __HAVE_ARCH_STRCASECMP
> -extern int strcasecmp(const char *s1, const char *s2);
> +extern int strcasecmp(const char *s1, const char *s2) __nocapture(1, 2);
> #endif
> #ifndef __HAVE_ARCH_STRNCASECMP
> -extern int strncasecmp(const char *s1, const char *s2, size_t n);
> +extern int
> +strncasecmp(const char *s1, const char *s2, size_t n) __nocapture(1, 2);
> #endif
> #ifndef __HAVE_ARCH_STRCHR
> extern char * strchr(const char *,int);
> @@ -72,44 +73,44 @@ static inline __must_check char *strstrip(char *str)
> }
>
> #ifndef __HAVE_ARCH_STRSTR
> -extern char * strstr(const char *, const char *);
> +extern char * strstr(const char *, const char *) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRNSTR
> -extern char * strnstr(const char *, const char *, size_t);
> +extern char * strnstr(const char *, const char *, size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRLEN
> -extern __kernel_size_t strlen(const char *);
> +extern __kernel_size_t strlen(const char *) __nocapture(1);
> #endif
> #ifndef __HAVE_ARCH_STRNLEN
> -extern __kernel_size_t strnlen(const char *,__kernel_size_t);
> +extern __kernel_size_t strnlen(const char *,__kernel_size_t) __nocapture(1);
> #endif
> #ifndef __HAVE_ARCH_STRPBRK
> -extern char * strpbrk(const char *,const char *);
> +extern char * strpbrk(const char *,const char *) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRSEP
> -extern char * strsep(char **,const char *);
> +extern char * strsep(char **,const char *) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_STRSPN
> -extern __kernel_size_t strspn(const char *,const char *);
> +extern __kernel_size_t strspn(const char *,const char *) __nocapture(1, 2);
> #endif
> #ifndef __HAVE_ARCH_STRCSPN
> -extern __kernel_size_t strcspn(const char *,const char *);
> +extern __kernel_size_t strcspn(const char *,const char *) __nocapture(1, 2);
> #endif
>
> #ifndef __HAVE_ARCH_MEMSET
> extern void * memset(void *,int,__kernel_size_t);
> #endif
> #ifndef __HAVE_ARCH_MEMCPY
> -extern void * memcpy(void *,const void *,__kernel_size_t);
> +extern void * memcpy(void *,const void *,__kernel_size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_MEMMOVE
> -extern void * memmove(void *,const void *,__kernel_size_t);
> +extern void * memmove(void *,const void *,__kernel_size_t) __nocapture(2);
> #endif
> #ifndef __HAVE_ARCH_MEMSCAN
> extern void * memscan(void *,int,__kernel_size_t);
> #endif
> #ifndef __HAVE_ARCH_MEMCMP
> -extern int memcmp(const void *,const void *,__kernel_size_t);
> +extern int memcmp(const void *,const void *,__kernel_size_t) __nocapture(1, 2);
> #endif
> #ifndef __HAVE_ARCH_MEMCHR
> extern void * memchr(const void *,int,__kernel_size_t);
> @@ -119,16 +120,16 @@ char *strreplace(char *s, char old, char new);
>
> extern void kfree_const(const void *x);
>
> -extern char *kstrdup(const char *s, gfp_t gfp) __malloc;
> -extern const char *kstrdup_const(const char *s, gfp_t gfp);
> -extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
> -extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
> +extern char *kstrdup(const char *s, gfp_t gfp) __malloc __nocapture(1);
> +extern const char *kstrdup_const(const char *s, gfp_t gfp) __nocapture(1);
> +extern char *kstrndup(const char *s, size_t len, gfp_t gfp) __nocapture(1);
> +extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __nocapture(1);
>
> extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
> extern void argv_free(char **argv);
>
> -extern bool sysfs_streq(const char *s1, const char *s2);
> -extern int kstrtobool(const char *s, bool *res);
> +extern bool sysfs_streq(const char *s1, const char *s2) __nocapture(1, 2);
> +extern int kstrtobool(const char *s, bool *res) __nocapture(1);
> static inline int strtobool(const char *s, bool *res)
> {
> return kstrtobool(s, res);
> @@ -137,8 +138,10 @@ static inline int strtobool(const char *s, bool *res)
> int match_string(const char * const *array, size_t n, const char *string);
>
> #ifdef CONFIG_BINARY_PRINTF
> -int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args);
> -int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf);
> +int vbin_printf(u32 *bin_buf, size_t size, const char *fmt,
> + va_list args) __nocapture(3);
> +int bstr_printf(char *buf, size_t size, const char *fmt,
> + const u32 *bin_buf) __nocapture(3);
> int bprintf(u32 *bin_buf, size_t size, const char *fmt, ...) __printf(3, 4);
> #endif
>
> --
> 2.8.1
>



--
Kees Cook
Chrome OS & Brillo Security