diff options
Diffstat (limited to 'lib/libc')
257 files changed, 9146 insertions, 2292 deletions
diff --git a/lib/libc/Makefile b/lib/libc/Makefile index d0c254e33396..8705568f6d34 100644 --- a/lib/libc/Makefile +++ b/lib/libc/Makefile @@ -109,7 +109,6 @@ NOASM= .include "${LIBC_SRCTOP}/inet/Makefile.inc" .include "${LIBC_SRCTOP}/isc/Makefile.inc" .include "${LIBC_SRCTOP}/locale/Makefile.inc" -.include "${LIBC_SRCTOP}/md/Makefile.inc" .include "${LIBC_SRCTOP}/nameser/Makefile.inc" .include "${LIBC_SRCTOP}/net/Makefile.inc" .include "${LIBC_SRCTOP}/nls/Makefile.inc" diff --git a/lib/libc/Versions.def b/lib/libc/Versions.def index 184e107d225a..1c7b34bef35b 100644 --- a/lib/libc/Versions.def +++ b/lib/libc/Versions.def @@ -42,6 +42,10 @@ FBSD_1.7 { FBSD_1.8 { } FBSD_1.7; +# This version was first added to 16.0-current. +FBSD_1.9 { +} FBSD_1.8; + # This is our private namespace. Any global interfaces that are # strictly for use only by other FreeBSD applications and libraries # are listed here. We use a separate namespace so we can write @@ -49,4 +53,4 @@ FBSD_1.8 { # # Please do NOT increment the version of this namespace. FBSDprivate_1.0 { -} FBSD_1.8; +} FBSD_1.9; diff --git a/lib/libc/aarch64/static_tls.h b/lib/libc/aarch64/static_tls.h deleted file mode 100644 index 2bb717eb75ce..000000000000 --- a/lib/libc/aarch64/static_tls.h +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by Konstantin Belousov <kib@FreeBSD.org> - * under sponsorship from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _LIBC_AARCH64_STATIC_TLS_H -#define _LIBC_AARCH64_STATIC_TLS_H - -static __inline uintptr_t -_libc_get_static_tls_base(size_t offset) -{ - uintptr_t tlsbase; - - __asm __volatile("mrs %x0, tpidr_el0" : "=r" (tlsbase)); - tlsbase += offset; - return (tlsbase); -} - -#endif diff --git a/lib/libc/aarch64/string/timingsafe_memcmp.S b/lib/libc/aarch64/string/timingsafe_memcmp.S index 28fdd911a387..4cc10a152aa8 100644 --- a/lib/libc/aarch64/string/timingsafe_memcmp.S +++ b/lib/libc/aarch64/string/timingsafe_memcmp.S @@ -114,4 +114,4 @@ ENTRY(timingsafe_memcmp) csetm w0, lo csinc w0, w0, wzr, ls ret -END(timingsafe_bcmp) +END(timingsafe_memcmp) diff --git a/lib/libc/amd64/static_tls.h b/lib/libc/amd64/static_tls.h deleted file mode 100644 index 1ee738b231c7..000000000000 --- a/lib/libc/amd64/static_tls.h +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by Konstantin Belousov <kib@FreeBSD.org> - * under sponsorship from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _LIBC_AMD64_STATIC_TLS_H -#define _LIBC_AMD64_STATIC_TLS_H - -static __inline uintptr_t -_libc_get_static_tls_base(size_t offset) -{ - uintptr_t tlsbase; - - __asm __volatile("movq %%fs:0, %0" : "=r" (tlsbase)); - tlsbase -= offset; - return (tlsbase); -} - -#endif diff --git a/lib/libc/amd64/string/memrchr.S b/lib/libc/amd64/string/memrchr.S index 4f6c5a238daa..80fb306af2a3 100644 --- a/lib/libc/amd64/string/memrchr.S +++ b/lib/libc/amd64/string/memrchr.S @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2023 Robert Clausecker + * Copyright (c) 2023, 2025 Robert Clausecker <fuz@FreeBSD.org> */ #include <machine/asm.h> @@ -16,150 +16,142 @@ ARCHFUNCS(memrchr) ENDARCHFUNCS(memrchr) ARCHENTRY(memrchr, scalar) - xor %eax, %eax # prospective return value - sub $4, %rdx # 4 bytes left to process? - jb 1f + lea -1(%rdi, %rdx, 1), %rax # point to last char in buffer + sub $4, %rdx # 4 bytes left to process? + jb .Ltail ALIGN_TEXT -0: xor %r8, %r8 - lea 2(%rdi), %r10 - cmp %sil, 2(%rdi) - cmovne %r8, %r10 # point to null if no match +0: cmp %sil, (%rax) # match at last entry? + je 1f - cmp %sil, (%rdi) - cmove %rdi, %r8 # point to first char if match + cmp %sil, -1(%rax) # match at second to last entry? + je 2f - lea 1(%rdi), %r9 - cmp %sil, 1(%rdi) - cmovne %r8, %r9 # point to first result if no match in second + cmp %sil, -2(%rax) # match at third to last entry? + je 3f - lea 3(%rdi), %r11 - cmp %sil, 3(%rdi) - cmovne %r10, %r11 + cmp %sil, -3(%rax) # match at fourth to last entry? + je 4f - test %r11, %r11 - cmovz %r9, %r11 # take first pair match if none in second + sub $4, %rax + sub $4, %rdx + jae 0b - test %r11, %r11 - cmovnz %r11, %rax # take match in current set if any +.Ltail: cmp $-3, %edx # at least one character left to process? + jb .Lnotfound - add $4, %rdi - sub $4, %rdx - jae 0b + cmp %sil, (%rax) + je 1f -1: cmp $-3, %edx # a least one character left to process? - jb 2f + cmp $-2, %edx # at least two characters left to process? + jb .Lnotfound - cmp %sil, (%rdi) - cmove %rdi, %rax + cmp %sil, -1(%rax) + je 2f - lea 1(%rdi), %rcx - cmp $-2, %edx # at least two characters left to process? - jb 2f + cmp $-1, %edx # at least three characters left to process? + jb .Lnotfound - cmp %sil, 1(%rdi) - cmove %rcx, %rax + cmp %sil, -2(%rax) + je 3f - lea 2(%rdi), %rcx - cmp $-1, %edx # at least three character left to process? - jb 2f - - cmp %sil, 2(%rdi) - cmove %rcx, %rax +.Lnotfound: + xor %eax, %eax + ret -2: ret + /* match found -- adjust rax to point to matching byte */ +4: dec %rax +3: dec %rax +2: dec %rax +1: ret ARCHEND(memrchr, scalar) ARCHENTRY(memrchr, baseline) - movd %esi, %xmm4 - test %rdx, %rdx # empty buffer? - jz .L0 # if yes, return immediately + test %rdx, %rdx # empty input? + je .Lnomatchb + + + lea (%rdi, %rdx, 1), %ecx # pointer to end of buffer + lea -1(%rdi, %rdx, 1), %rdx # pointer to last char in buffer + movd %esi, %xmm2 + and $~0x1f, %rdx # pointer to final 32 buffer bytes + movdqa (%rdx), %xmm0 # load last 32 bytes + movdqa 16(%rdx), %xmm1 + + punpcklbw %xmm2, %xmm2 # c -> cc - punpcklbw %xmm4, %xmm4 # c -> cc - mov %edi, %ecx - punpcklwd %xmm4, %xmm4 # cc -> cccc - and $~0xf, %rdi # align source pointer - pshufd $0, %xmm4, %xmm4 # cccc -> cccccccccccccccc - and $0xf, %ecx - movdqa %xmm4, %xmm0 mov $-1, %r8d - pcmpeqb (%rdi), %xmm0 # compare aligned head - shl %cl, %r8d # mask of bytes in the head of the buffer - pmovmskb %xmm0, %eax + neg %ecx + mov %r8d, %r9d + shr %cl, %r8d # mask with zeroes after the string - sub $16, %rcx - and %r8d, %eax # match mask - add %rcx, %rdx # advance past head - cmc - jbe .Lrunt # did the string end in the buffer? + punpcklwd %xmm2, %xmm2 # cc -> cccc - mov %rdi, %rsi # pointer to matching chunk - add $16, %rdi - sub $16, %rdx # enough left for another round? - jbe 1f + mov %edi, %ecx + mov %r9d, %eax + shl %cl, %r9d # mask with zeroes before the string - /* main loop unrolled twice */ - ALIGN_TEXT -0: movdqa %xmm4, %xmm0 - pcmpeqb (%rdi), %xmm0 - pmovmskb %xmm0, %r8d + pshufd $0, %xmm2, %xmm2 # cccc -> cccccccccccccccc - cmp $16, %rdx # enough left for second chunk? - jbe 2f + cmp %rdx, %rdi # tail is beginning of buffer? + cmovae %r9d, %eax # if yes, do combined head/tail processing + and %r8d, %eax # mak of bytes in tail part of string - movdqa %xmm4, %xmm0 - pcmpeqb 16(%rdi), %xmm0 + /* process tail */ + pcmpeqb %xmm2, %xmm1 + pcmpeqb %xmm2, %xmm0 + pmovmskb %xmm1, %esi pmovmskb %xmm0, %ecx + shl $16, %esi + or %esi, %ecx # locations of matches + and %ecx, %eax # any match inside buffer? + jnz .Lprecisematchb - lea 16(%rdi), %r9 - test %ecx, %ecx # match found in second chunk? - cmovz %r8d, %ecx # if not, use match data from first chunk - cmovz %rdi, %r9 - - test %ecx, %ecx # any match found? - cmovnz %ecx, %eax # if yes, overwrite previously found match - cmovnz %r9, %rsi - - add $32, %rdi # advance to next iteration - sub $32, %rdx # advance to next chunks - ja 0b - - /* process remaining 1--16 bytes */ -1: pcmpeqb (%rdi), %xmm4 - mov $0xffff, %r8d - xor %ecx, %ecx - sub %edx, %ecx # number of bytes to be masked out - pmovmskb %xmm4, %r9d - shr %cl, %r8d # mask of bytes to be kept in the buffer - and %r9d, %r8d - cmovnz %r8d, %eax - cmovnz %rdi, %rsi - bsr %eax, %eax - lea (%rsi, %rax, 1), %rsi # pointer to match (or junk) - cmovnz %rsi, %rax # if any match was found, return it - ret + cmp %rdx, %rdi # did the buffer begin here? + jae .Lnomatchb # if yes, we are done - /* end of chunk reached within first half iteration */ -2: test %r8d, %r8d # match in previous chunk? - cmovnz %r8d, %eax # if yes, overwrite previous chunks - cmovnz %rdi, %rsi - add $16, %rdi # point to tail - sub $16, %edx - jmp 1b # handle tail the same otherwise - - /* runt: string ends within head, edx has negated amount of invalid head bytes */ -.Lrunt: mov $0xffff, %r8d - xor %ecx, %ecx - sub %edx, %ecx - shr %cl, %r8d - and %r8d, %eax - bsr %eax, %eax - lea (%rdi, %rax, 1), %rdi - cmovnz %rdi, %rax + /* main loop */ + ALIGN_TEXT +0: movdqa -32(%rdx), %xmm0 # load previous string chunk + movdqa -16(%rdx), %xmm1 + sub $32, %rdx # beginning of string reached? + cmp %rdx, %rdi + jae .Ltailb + + pcmpeqb %xmm2, %xmm0 + pcmpeqb %xmm2, %xmm1 + por %xmm1, %xmm0 # match in either half? + pmovmskb %xmm0, %eax + test %eax, %eax + jz 0b + +.Lmatchb: + pcmpeqb (%rdx), %xmm2 # redo comparison of first 16 bytes + pmovmskb %xmm1, %ecx + pmovmskb %xmm2, %eax + shl $16, %ecx + or %ecx, %eax # location of matches + +.Lprecisematchb: + bsr %eax, %eax # find location of match + add %rdx, %rax # point to matching byte ret - /* empty buffer: return a null pointer */ -.L0: xor %eax, %eax +.Ltailb: + pcmpeqb %xmm2, %xmm1 + pcmpeqb %xmm2, %xmm0 + pmovmskb %xmm1, %ecx + pmovmskb %xmm0, %eax + shl $16, %ecx + or %ecx, %eax # location of matches + and %r9d, %eax # mask out matches before buffer + bsr %eax, %edi # location of match + lea (%rdx, %rdi, 1), %rdx # pointer to match (if any) + cmovnz %rdx, %rax # point to match if present, + ret # else null pointer + +.Lnomatchb: + xor %eax, %eax # return null pointer ret ARCHEND(memrchr, baseline) diff --git a/lib/libc/arm/aeabi/Symbol.map b/lib/libc/arm/aeabi/Symbol.map index 175884c82881..515794004ba7 100644 --- a/lib/libc/arm/aeabi/Symbol.map +++ b/lib/libc/arm/aeabi/Symbol.map @@ -6,50 +6,6 @@ FBSDprivate_1.0 { __aeabi_atexit; - __aeabi_dcmpeq; - __aeabi_dcmplt; - __aeabi_dcmple; - __aeabi_dcmpge; - __aeabi_dcmpgt; - __aeabi_dcmpun; - - __aeabi_cdcmpeq; - __aeabi_cdcmple; - __aeabi_cdrcmple; - - __aeabi_d2iz; - __aeabi_d2f; - - __aeabi_dadd; - __aeabi_ddiv; - __aeabi_dmul; - __aeabi_dsub; - - - __aeabi_fcmpeq; - __aeabi_fcmplt; - __aeabi_fcmple; - __aeabi_fcmpge; - __aeabi_fcmpgt; - __aeabi_fcmpun; - - __aeabi_cfcmpeq; - __aeabi_cfcmple; - __aeabi_cfrcmple; - - __aeabi_f2iz; - __aeabi_f2d; - - __aeabi_fadd; - __aeabi_fdiv; - __aeabi_fmul; - __aeabi_fsub; - - - __aeabi_i2d; - __aeabi_i2f; - - __aeabi_memclr; __aeabi_memclr4; __aeabi_memclr8; diff --git a/lib/libc/arm/aeabi/aeabi_int_div.S b/lib/libc/arm/aeabi/aeabi_int_div.S index 708b478b3881..bb002c2716f7 100644 --- a/lib/libc/arm/aeabi/aeabi_int_div.S +++ b/lib/libc/arm/aeabi/aeabi_int_div.S @@ -37,11 +37,13 @@ */ ENTRY(__aeabi_uidiv_compat) + .hidden __aeabi_uidiv_compat .symver __aeabi_uidiv_compat, __aeabi_uidiv@ b __udivsi3 END(__aeabi_uidiv_compat) ENTRY(__aeabi_idiv_compat) + .hidden __aeabi_idiv_compat .symver __aeabi_idiv_compat, __aeabi_idiv@ b __divsi3 END(__aeabi_idiv_compat) diff --git a/lib/libc/arm/aeabi/aeabi_vfp.h b/lib/libc/arm/aeabi/aeabi_vfp.h index 3b70fe06fab3..4f3bb2ef969b 100644 --- a/lib/libc/arm/aeabi/aeabi_vfp.h +++ b/lib/libc/arm/aeabi/aeabi_vfp.h @@ -40,7 +40,9 @@ #define AEABI_ENTRY(x) ENTRY(__aeabi_ ## x ## _vfp) #define AEABI_END(x) END(__aeabi_ ## x ## _vfp) #else -#define AEABI_ENTRY(x) ENTRY(__aeabi_ ## x) +#define AEABI_ENTRY(x) \ + ENTRY(__aeabi_ ## x) \ + .symver __aeabi_##x, __aeabi_##x##@FBSDprivate_1.0; #define AEABI_END(x) END(__aeabi_ ## x) #endif diff --git a/lib/libc/arm/static_tls.h b/lib/libc/arm/static_tls.h deleted file mode 100644 index 557748d75347..000000000000 --- a/lib/libc/arm/static_tls.h +++ /dev/null @@ -1,45 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by Konstantin Belousov <kib@FreeBSD.org> - * under sponsorship from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _LIBC_ARM_STATIC_TLS_H -#define _LIBC_ARM_STATIC_TLS_H - -static __inline uintptr_t -_libc_get_static_tls_base(size_t offset) -{ - uintptr_t tlsbase; - - __asm __volatile("mrc p15, 0, %0, c13, c0, 3" : "=r" (tlsbase)); - - tlsbase += offset; - return (tlsbase); -} - -#endif diff --git a/lib/libc/csu/libc_start1.c b/lib/libc/csu/libc_start1.c index 045ea1e68141..5b08ba03ba8c 100644 --- a/lib/libc/csu/libc_start1.c +++ b/lib/libc/csu/libc_start1.c @@ -28,6 +28,7 @@ #include <sys/param.h> #include <sys/elf.h> #include <sys/elf_common.h> +#include <errno.h> #include <stdlib.h> #include "libc_private.h" @@ -169,6 +170,13 @@ __libc_start1(int argc, char *argv[], char *env[], void (*cleanup)(void), } handle_static_init(argc, argv, env); + + /* + * C17 4.3 paragraph 3: + * The value of errno in the initial thread is zero at program + * startup. + */ + errno = 0; exit(mainX(argc, argv, env)); } @@ -194,5 +202,6 @@ __libc_start1_gcrt(int argc, char *argv[], char *env[], monstartup(eprolp, etextp); handle_static_init(argc, argv, env); + errno = 0; exit(mainX(argc, argv, env)); } diff --git a/lib/libc/db/hash/hash.c b/lib/libc/db/hash/hash.c index 7a66f5443d94..88a3ffeab828 100644 --- a/lib/libc/db/hash/hash.c +++ b/lib/libc/db/hash/hash.c @@ -99,11 +99,6 @@ __hash_open(const char *file, int flags, int mode, DB *dbp; int bpages, hdrsize, new_table, nsegs, save_errno; - if ((flags & O_ACCMODE) == O_WRONLY) { - errno = EINVAL; - return (NULL); - } - if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) return (NULL); hashp->fp = -1; @@ -115,12 +110,17 @@ __hash_open(const char *file, int flags, int mode, * we can check accesses. */ hashp->flags = flags; + if ((flags & O_ACCMODE) == O_WRONLY) { + flags &= ~O_WRONLY; + flags |= O_RDWR; + } if (file) { if ((hashp->fp = _open(file, flags | O_CLOEXEC, mode)) == -1) RETURN_ERROR(errno, error0); new_table = _fstat(hashp->fp, &statbuf) == 0 && - statbuf.st_size == 0 && (flags & O_ACCMODE) != O_RDONLY; + statbuf.st_size == 0 && + ((flags & O_ACCMODE) != O_RDONLY || (flags & O_CREAT) != 0); } else new_table = 1; @@ -179,7 +179,7 @@ __hash_open(const char *file, int flags, int mode, __buf_init(hashp, DEF_BUFSIZE); hashp->new_file = new_table; - hashp->save_file = file && (hashp->flags & O_RDWR); + hashp->save_file = file && (flags & O_RDWR); hashp->cbucket = -1; if (!(dbp = (DB *)malloc(sizeof(DB)))) { save_errno = errno; @@ -523,6 +523,10 @@ hash_get(const DB *dbp, const DBT *key, DBT *data, u_int32_t flag) hashp->error = errno = EINVAL; return (ERROR); } + if ((hashp->flags & O_ACCMODE) == O_WRONLY) { + hashp->error = errno = EPERM; + return (ERROR); + } return (hash_access(hashp, HASH_GET, (DBT *)key, data)); } @@ -700,17 +704,19 @@ hash_seq(const DB *dbp, DBT *key, DBT *data, u_int32_t flag) u_int16_t *bp, ndx; hashp = (HTAB *)dbp->internal; - if (flag && flag != R_FIRST && flag != R_NEXT) { + if (flag != R_FIRST && flag != R_NEXT) { hashp->error = errno = EINVAL; return (ERROR); } #ifdef HASH_STATISTICS hash_accesses++; #endif - if ((hashp->cbucket < 0) || (flag == R_FIRST)) { + if (flag == R_FIRST) { hashp->cbucket = 0; hashp->cndx = 1; hashp->cpage = NULL; + } else if (hashp->cbucket < 0) { /* R_NEXT */ + return (ABNORMAL); } next_bucket: for (bp = NULL; !bp || !bp[0]; ) { diff --git a/lib/libc/db/man/dbm.3 b/lib/libc/db/man/dbm.3 index c5a83c7acef4..30787600ad2d 100644 --- a/lib/libc/db/man/dbm.3 +++ b/lib/libc/db/man/dbm.3 @@ -13,7 +13,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 2, 2022 +.Dd July 25, 2025 .Dt DBM 3 .Os .Sh NAME @@ -99,9 +99,6 @@ is a typical value for .Li 0660 is a typical value for .Fa mode . -.Dv O_WRONLY -is not allowed in -.Fa flags . The pointer returned by .Fn dbm_open identifies the database and is the diff --git a/lib/libc/db/man/dbopen.3 b/lib/libc/db/man/dbopen.3 index 64cef88506d8..7fe515f17849 100644 --- a/lib/libc/db/man/dbopen.3 +++ b/lib/libc/db/man/dbopen.3 @@ -76,13 +76,10 @@ are as specified to the .Xr open 2 routine, however, only the .Dv O_CREAT , O_EXCL , O_EXLOCK , O_NOFOLLOW , O_NONBLOCK , -.Dv O_RDONLY , O_RDWR , O_SHLOCK , O_SYNC +.Dv O_RDONLY , O_RDWR , O_SHLOCK , O_SYNC, O_WRONLY, and .Dv O_TRUNC flags are meaningful. -(Note, opening a database file -.Dv O_WRONLY -is not possible.) .\"Three additional options may be specified by .\".Em or Ns 'ing .\"them into the diff --git a/lib/libc/gdtoa/_hdtoa.c b/lib/libc/gdtoa/_hdtoa.c index 8ae739acf0db..9c42630cd918 100644 --- a/lib/libc/gdtoa/_hdtoa.c +++ b/lib/libc/gdtoa/_hdtoa.c @@ -40,6 +40,7 @@ #define DBL_ADJ (DBL_MAX_EXP - 2) #define SIGFIGS ((DBL_MANT_DIG + 3) / 4 + 1) +#define MAX_HEX_DIGITS ((DBL_MANT_DIG + 3 - 1) / 4 + 1) static const float one[] = { 1.0f, -1.0f }; @@ -111,7 +112,7 @@ __hdtoa(double d, const char *xdigs, int ndigits, int *decpt, int *sign, s0 = rv_alloc(bufsize); /* Round to the desired number of digits. */ - if (SIGFIGS > ndigits && ndigits > 0) { + if (MAX_HEX_DIGITS > ndigits && ndigits > 0) { float redux = one[u.bits.sign]; int offset = 4 * ndigits + DBL_MAX_EXP - 4 - DBL_MANT_DIG; u.bits.exp = offset; diff --git a/lib/libc/gdtoa/_hldtoa.c b/lib/libc/gdtoa/_hldtoa.c index 965d2349d103..5f10d12c5c09 100644 --- a/lib/libc/gdtoa/_hldtoa.c +++ b/lib/libc/gdtoa/_hldtoa.c @@ -65,6 +65,7 @@ typedef uint32_t manl_t; #define LDBL_ADJ (LDBL_MAX_EXP - 2) #define SIGFIGS ((LDBL_MANT_DIG + 3) / 4 + 1) +#define MAX_HEX_DIGITS ((LDBL_MANT_DIG + 3 - 1) / 4 + 1) static const float one[] = { 1.0f, -1.0f }; @@ -125,7 +126,7 @@ __hldtoa(long double e, const char *xdigs, int ndigits, int *decpt, int *sign, s0 = rv_alloc(bufsize); /* Round to the desired number of digits. */ - if (SIGFIGS > ndigits && ndigits > 0) { + if (MAX_HEX_DIGITS > ndigits && ndigits > 0) { float redux = one[u.bits.sign]; int offset = 4 * ndigits + LDBL_MAX_EXP - 4 - LDBL_MANT_DIG; #ifdef __i386__ diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 9b92e52cdfad..4d064d18d36e 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -89,6 +89,7 @@ SRCS+= \ glob.c \ glob-compat11.c \ initgroups.c \ + inotify.c \ isatty.c \ isinf.c \ isnan.c \ @@ -134,6 +135,7 @@ SRCS+= \ setmode.c \ setproctitle.c \ setprogname.c \ + sig2str.c \ siginterrupt.c \ siglist.c \ signal.c \ @@ -159,6 +161,8 @@ SRCS+= \ ttyname.c \ ttyslot.c \ ualarm.c \ + uexterr_format.c \ + uexterr_gettext.c \ ulimit.c \ uname.c \ unvis-compat.c \ @@ -168,6 +172,11 @@ SRCS+= \ valloc.c \ wordexp.c +.if ${COMPILER_FEATURES:Mblocks} +CFLAGS.fts.c= -fblocks +CFLAGS.glob.c= -fblocks +.endif + CFLAGS.arc4random.c= -I${SRCTOP}/sys -I${SRCTOP}/sys/crypto/chacha20 CFLAGS.sysconf.c= -I${SRCTOP}/contrib/tzcode @@ -362,7 +371,8 @@ MLINKS+=exec.3 execl.3 \ exec.3 exect.3 \ exec.3 execv.3 \ exec.3 execvP.3 \ - exec.3 execvp.3 + exec.3 execvp.3 \ + exec.3 execvpe.3 MLINKS+=fpclassify.3 finite.3 \ fpclassify.3 finitef.3 \ fpclassify.3 isfinite.3 \ @@ -465,7 +475,10 @@ MLINKS+=posix_spawn.3 posix_spawnp.3 \ posix_spawnattr_getsigdefault.3 posix_spawnattr_setsigdefault.3 \ posix_spawnattr_getsigmask.3 posix_spawnattr_setsigmask.3 \ posix_spawnattr_init.3 posix_spawnattr_destroy.3 -MLINKS+=psignal.3 strsignal.3 \ +MLINKS+=psignal.3 psiginfo.3 \ + psignal.3 sig2str.3 \ + psignal.3 str2sig.3 \ + psignal.3 strsignal.3 \ psignal.3 sys_siglist.3 \ psignal.3 sys_signame.3 MLINKS+=pwcache.3 gid_from_group.3 \ @@ -487,8 +500,11 @@ MLINKS+=rand48.3 _rand48.3 \ MLINKS+=rtld_get_var.3 \ rtld_set_var.3 MLINKS+=scandir.3 alphasort.3 \ - scandir.3 scandirat.3 \ + scandir.3 fdscandir.3 \ + scandir.3 fdscandir_b.3 \ scandir.3 scandir_b.3 \ + scandir.3 scandirat.3 \ + scandir.3 scandirat_b.3 \ scandir.3 versionsort.3 MLINKS+=sem_open.3 sem_close.3 \ sem_open.3 sem_unlink.3 diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index ca3974e6b747..494b65bc5cc1 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -193,7 +193,6 @@ FBSD_1.0 { __isinff; __isinfl; isatty; - initgroups; jrand48; lcong48; ldexp; @@ -458,8 +457,21 @@ FBSD_1.8 { aio_read2; aio_write2; execvpe; + fdscandir; + fdscandir_b; + fts_open_b; + glob_b; + initgroups; + inotify_add_watch; + inotify_init; + inotify_init1; + psiginfo; rtld_get_var; rtld_set_var; + scandirat_b; + uexterr_gettext; + sig2str; + str2sig; }; FBSDprivate_1.0 { @@ -584,10 +596,10 @@ FBSDprivate_1.0 { __libc_tcdrain; - __pthread_distribute_static_tls; __pthread_map_stacks_exec; __fillcontextx; __fillcontextx2; __getcontextx_size; __makecontext; + __uexterr_format; }; diff --git a/lib/libc/gen/_pthread_stubs.c b/lib/libc/gen/_pthread_stubs.c index 2a0cebadd5fd..d867ee4db51e 100644 --- a/lib/libc/gen/_pthread_stubs.c +++ b/lib/libc/gen/_pthread_stubs.c @@ -63,6 +63,7 @@ static int stub_getname_np(pthread_t, char *, size_t); #define PJT_DUAL_ENTRY(entry) \ (pthread_func_t)entry, (pthread_func_t)entry +__attribute__((visibility("protected"))) pthread_func_entry_t __thr_jtable[PJT_MAX] = { [PJT_ATFORK] = {PJT_DUAL_ENTRY(stub_zero)}, [PJT_ATTR_DESTROY] = {PJT_DUAL_ENTRY(stub_zero)}, diff --git a/lib/libc/gen/_rand48.c b/lib/libc/gen/_rand48.c index 990e2c86949b..114c1595b33d 100644 --- a/lib/libc/gen/_rand48.c +++ b/lib/libc/gen/_rand48.c @@ -13,34 +13,6 @@ #include "rand48.h" -unsigned short _rand48_seed[3] = { - RAND48_SEED_0, - RAND48_SEED_1, - RAND48_SEED_2 -}; -unsigned short _rand48_mult[3] = { - RAND48_MULT_0, - RAND48_MULT_1, - RAND48_MULT_2 -}; -unsigned short _rand48_add = RAND48_ADD; - -void -_dorand48(unsigned short xseed[3]) -{ - unsigned long accu; - unsigned short temp[2]; - - accu = (unsigned long) _rand48_mult[0] * (unsigned long) xseed[0] + - (unsigned long) _rand48_add; - temp[0] = (unsigned short) accu; /* lower 16 bits */ - accu >>= sizeof(unsigned short) * 8; - accu += (unsigned long) _rand48_mult[0] * (unsigned long) xseed[1] + - (unsigned long) _rand48_mult[1] * (unsigned long) xseed[0]; - temp[1] = (unsigned short) accu; /* middle 16 bits */ - accu >>= sizeof(unsigned short) * 8; - accu += _rand48_mult[0] * xseed[2] + _rand48_mult[1] * xseed[1] + _rand48_mult[2] * xseed[0]; - xseed[0] = temp[0]; - xseed[1] = temp[1]; - xseed[2] = (unsigned short) accu; -} +uint48 _rand48_seed = RAND48_SEED; +uint48 _rand48_mult = RAND48_MULT; +uint48 _rand48_add = RAND48_ADD; diff --git a/lib/libc/gen/alarm.3 b/lib/libc/gen/alarm.3 index 8aeda29481eb..4a24cc6d7216 100644 --- a/lib/libc/gen/alarm.3 +++ b/lib/libc/gen/alarm.3 @@ -63,7 +63,7 @@ Due to .Xr setitimer 2 restriction the maximum number of .Fa seconds -allowed is 100000000. +allowed is 100,000,000. .Sh RETURN VALUES The return value of .Fn alarm diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c index bffee3952e0d..ae1c8d83df19 100644 --- a/lib/libc/gen/dlfcn.c +++ b/lib/libc/gen/dlfcn.c @@ -229,7 +229,7 @@ _dl_iterate_phdr_locked( return (1); _once(&dl_phdr_info_once, dl_init_phdr_info); ti.ti_module = 1; - ti.ti_offset = 0; + ti.ti_offset = -TLS_DTV_OFFSET; phdr_info.dlpi_tls_data = __tls_get_addr(&ti); ret = callback(&phdr_info, sizeof(phdr_info), data); return (ret); diff --git a/lib/libc/gen/drand48.c b/lib/libc/gen/drand48.c index cec04a6a2425..f7f43ff20468 100644 --- a/lib/libc/gen/drand48.c +++ b/lib/libc/gen/drand48.c @@ -13,10 +13,10 @@ #include "rand48.h" -extern unsigned short _rand48_seed[3]; - double drand48(void) { - return erand48(_rand48_seed); + ERAND48_BEGIN; + _DORAND48(_rand48_seed); + ERAND48_END(_rand48_seed); } diff --git a/lib/libc/gen/dup3.3 b/lib/libc/gen/dup3.3 index f2798930797b..338a9ae74c64 100644 --- a/lib/libc/gen/dup3.3 +++ b/lib/libc/gen/dup3.3 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd August 16, 2013 +.Dd May 17, 2025 .Dt DUP3 3 .Os .Sh NAME @@ -47,6 +47,11 @@ The close-on-exec flag on the new file descriptor is determined by the bit in .Fa flags . .Pp +The close-on-fork flag on the new file descriptor is determined by the +.Dv O_CLOFORK +bit in +.Fa flags . +.Pp If .Fa oldd \*(Ne @@ -91,7 +96,9 @@ argument. The .Fa flags argument has bits set other than -.Dv O_CLOEXEC . +.Dv O_CLOEXEC +or +.Dv O_CLOFORK . .El .Sh SEE ALSO .Xr accept 2 , @@ -112,3 +119,7 @@ The .Fn dup3 function appeared in .Fx 10.0 . +The +.Dv O_CLOFORK +flag appeared in +.Fx 15.0 . diff --git a/lib/libc/gen/dup3.c b/lib/libc/gen/dup3.c index fca1e99fb47b..1401c1f5b607 100644 --- a/lib/libc/gen/dup3.c +++ b/lib/libc/gen/dup3.c @@ -39,21 +39,22 @@ int __dup3(int, int, int); int __dup3(int oldfd, int newfd, int flags) { - int how; + int fdflags; if (oldfd == newfd) { errno = EINVAL; return (-1); } - if (flags & ~O_CLOEXEC) { + if ((flags & ~(O_CLOEXEC | O_CLOFORK)) != 0) { errno = EINVAL; return (-1); } - how = (flags & O_CLOEXEC) ? F_DUP2FD_CLOEXEC : F_DUP2FD; + fdflags = ((flags & O_CLOEXEC) != 0 ? FD_CLOEXEC : 0) | + ((flags & O_CLOFORK) != 0 ? FD_CLOFORK : 0); - return (_fcntl(oldfd, how, newfd)); + return (_fcntl(oldfd, F_DUP3FD | (fdflags << F_DUP3FD_SHIFT), newfd)); } __weak_reference(__dup3, dup3); diff --git a/lib/libc/gen/elf_utils.c b/lib/libc/gen/elf_utils.c index 5b87e012d0eb..3714a0dc42b5 100644 --- a/lib/libc/gen/elf_utils.c +++ b/lib/libc/gen/elf_utils.c @@ -31,14 +31,16 @@ #include <sys/mman.h> #include <sys/resource.h> #include <sys/sysctl.h> + +#include <machine/tls.h> + #include <link.h> #include <stddef.h> #include <string.h> + #include "libc_private.h" -#include "static_tls.h" void __pthread_map_stacks_exec(void); -void __pthread_distribute_static_tls(size_t, void *, size_t, size_t); int __elf_phdr_match_addr(struct dl_phdr_info *phdr_info, void *addr) @@ -102,24 +104,3 @@ __pthread_map_stacks_exec(void) ((void (*)(void))__libc_interposing[INTERPOS_map_stacks_exec])(); } - -void -__libc_distribute_static_tls(size_t offset, void *src, size_t len, - size_t total_len) -{ - uintptr_t tlsbase; - - tlsbase = _libc_get_static_tls_base(offset); - memcpy((void *)tlsbase, src, len); - memset((char *)tlsbase + len, 0, total_len - len); -} - -#pragma weak __pthread_distribute_static_tls -void -__pthread_distribute_static_tls(size_t offset, void *src, size_t len, - size_t total_len) -{ - - ((void (*)(size_t, void *, size_t, size_t))__libc_interposing[ - INTERPOS_distribute_static_tls])(offset, src, len, total_len); -} diff --git a/lib/libc/gen/erand48.c b/lib/libc/gen/erand48.c index 286904c27839..38d4774a9fe6 100644 --- a/lib/libc/gen/erand48.c +++ b/lib/libc/gen/erand48.c @@ -16,8 +16,9 @@ double erand48(unsigned short xseed[3]) { - _dorand48(xseed); - return ldexp((double) xseed[0], -48) + - ldexp((double) xseed[1], -32) + - ldexp((double) xseed[2], -16); + uint48 tmp; + + ERAND48_BEGIN; + DORAND48(tmp, xseed); + ERAND48_END(tmp); } diff --git a/lib/libc/gen/err.c b/lib/libc/gen/err.c index 24ea242560b8..16cbe27693e7 100644 --- a/lib/libc/gen/err.c +++ b/lib/libc/gen/err.c @@ -30,9 +30,12 @@ */ #include "namespace.h" +#include <sys/exterrvar.h> #include <err.h> #include <errno.h> +#include <exterr.h> #include <stdarg.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -43,6 +46,11 @@ static FILE *err_file; /* file to use for error output */ static void (*err_exit)(int); +static void verrci(bool doexterr, int eval, int code, const char *fmt, + va_list ap) __printf0like(4, 0) __dead2; +static void vwarnci(bool doexterr, int code, const char *fmt, va_list ap) + __printf0like(3, 0); + /* * This is declared to take a `void *' so that the caller is not required * to include <stdio.h> first. However, it is really a `FILE *', and the @@ -70,14 +78,14 @@ _err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - verrc(eval, errno, fmt, ap); + verrci(true, eval, errno, fmt, ap); va_end(ap); } void verr(int eval, const char *fmt, va_list ap) { - verrc(eval, errno, fmt, ap); + verrci(true, eval, errno, fmt, ap); } void @@ -85,13 +93,24 @@ errc(int eval, int code, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - verrc(eval, code, fmt, ap); + verrci(false, eval, code, fmt, ap); va_end(ap); } void verrc(int eval, int code, const char *fmt, va_list ap) { + verrci(false, eval, code, fmt, ap); +} + +static void +vexterr(bool doexterr, int code, const char *fmt, va_list ap) +{ + char exterr[UEXTERROR_MAXLEN]; /* libc knows the buffer size */ + int extstatus; + + if (doexterr) + extstatus = uexterr_gettext(exterr, sizeof(exterr)); if (err_file == NULL) err_set_file(NULL); fprintf(err_file, "%s: ", _getprogname()); @@ -99,7 +118,16 @@ verrc(int eval, int code, const char *fmt, va_list ap) vfprintf(err_file, fmt, ap); fprintf(err_file, ": "); } - fprintf(err_file, "%s\n", strerror(code)); + fprintf(err_file, "%s", strerror(code)); + if (doexterr && extstatus == 0 && exterr[0] != '\0') + fprintf(err_file, " (extended error %s)", exterr); + fprintf(err_file, "\n"); +} + +static void +verrci(bool doexterr, int eval, int code, const char *fmt, va_list ap) +{ + vexterr(doexterr, code, fmt, ap); if (err_exit) err_exit(eval); exit(eval); @@ -157,17 +185,16 @@ warnc(int code, const char *fmt, ...) void vwarnc(int code, const char *fmt, va_list ap) { + vwarnci(false, code, fmt, ap); +} + +static void +vwarnci(bool doexterr, int code, const char *fmt, va_list ap) +{ int saved_errno; saved_errno = errno; - if (err_file == NULL) - err_set_file(NULL); - fprintf(err_file, "%s: ", _getprogname()); - if (fmt != NULL) { - vfprintf(err_file, fmt, ap); - fprintf(err_file, ": "); - } - fprintf(err_file, "%s\n", strerror(code)); + vexterr(doexterr, code, fmt, ap); errno = saved_errno; } diff --git a/lib/libc/gen/exec.c b/lib/libc/gen/exec.c index d9285733e965..12020a79f6b4 100644 --- a/lib/libc/gen/exec.c +++ b/lib/libc/gen/exec.c @@ -32,8 +32,10 @@ #include "namespace.h" #include <sys/param.h> #include <sys/stat.h> +#include <assert.h> #include <errno.h> #include <unistd.h> +#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <stdio.h> @@ -136,29 +138,132 @@ execv(const char *name, char * const *argv) int execvp(const char *name, char * const *argv) { - return (execvpe(name, argv, environ)); + return (__libc_execvpe(name, argv, environ)); +} + +/* + * Returns 0 if we don't consider this a terminal condition, -1 if we do. + */ +static int +execvPe_prog(const char *path, char * const *argv, char * const *envp) +{ + struct stat sb; + const char **memp; + size_t cnt; + int save_errno; + + (void)_execve(path, argv, envp); + /* Grouped roughly by never terminal vs. usually terminal conditions */ + switch (errno) { + case ELOOP: + case ENAMETOOLONG: + case ENOENT: + case ENOTDIR: + /* Non-terminal: property of the path we're trying */ + break; + case ENOEXEC: + /* + * Failures here are considered terminal because we must handle + * this via the ENOEXEC fallback path; doing any further + * searching would be categorically incorrect. + */ + + for (cnt = 0; argv[cnt] != NULL; ++cnt) + ; + + /* + * cnt may be 0 above; always allocate at least + * 3 entries so that we can at least fit "sh", path, and + * the NULL terminator. We can rely on cnt to take into + * account the NULL terminator in all other scenarios, + * as we drop argv[0]. + */ + memp = alloca(MAX(3, cnt + 2) * sizeof(char *)); + assert(memp != NULL); + if (cnt > 0) { + memp[0] = argv[0]; + memp[1] = path; + memcpy(&memp[2], &argv[1], cnt * sizeof(char *)); + } else { + memp[0] = "sh"; + memp[1] = path; + memp[2] = NULL; + } + + (void)_execve(_PATH_BSHELL, __DECONST(char **, memp), envp); + return (-1); + case ENOMEM: + case E2BIG: + /* Terminal: persistent condition */ + return (-1); + case ETXTBSY: + /* + * Terminal: we used to retry here, but sh(1) doesn't. + */ + return (-1); + default: + /* + * EACCES may be for an inaccessible directory or + * a non-executable file. Call stat() to decide + * which. This also handles ambiguities for EFAULT + * and EIO, and undocumented errors like ESTALE. + * We hope that the race for a stat() is unimportant. + */ + save_errno = errno; + if (stat(path, &sb) == -1) { + /* + * We force errno to ENOENT here to disambiguate the + * EACCESS case; the results of execve(2) are somewhat + * inconclusive because either the file did not exist or + * we just don't have search permissions, but the caller + * only really wants to see EACCES if the file did exist + * but was not accessible. + */ + if (save_errno == EACCES) + errno = ENOENT; + break; + } + + errno = save_errno; + + /* + * Non-terminal: the file did exist and we just didn't have + * access to it, so we surface the EACCES and let the search + * continue for a candidate that we do have access to. + */ + if (errno == EACCES) + break; + + /* + * All other errors here are terminal, as prescribed by exec(3). + */ + return (-1); + } + + return (0); } static int execvPe(const char *name, const char *path, char * const *argv, char * const *envp) { - const char **memp; - size_t cnt, lp, ln; - int eacces, save_errno; char buf[MAXPATHLEN]; - const char *bp, *np, *op, *p; - struct stat sb; + size_t ln, lp; + const char *np, *op, *p; + bool eacces; - eacces = 0; + eacces = false; /* If it's an absolute or relative path name, it's easy. */ - if (strchr(name, '/')) { - bp = name; - op = NULL; - goto retry; + if (strchr(name, '/') != NULL) { + /* + * We ignore non-terminal conditions because we don't have any + * further paths to try -- we can just bubble up the errno from + * execve(2) here. + */ + (void)execvPe_prog(name, argv, envp); + return (-1); } - bp = buf; /* If it's an empty path name, fail in the usual POSIX way. */ if (*name == '\0') { @@ -192,9 +297,13 @@ execvPe(const char *name, const char *path, char * const *argv, op = np + 1; /* - * If the path is too long complain. This is a possible - * security issue; given a way to make the path too long - * the user may execute the wrong program. + * If the path is too long, then complain. This is a possible + * security issue: given a way to make the path too long, the + * user may execute the wrong program. + * + * Remember to exercise caution here with assembling our final + * buf and any output, as we may be running in a vfork() context + * via posix_spawnp(). */ if (lp + ln + 2 > sizeof(buf)) { (void)_write(STDERR_FILENO, execvPe_err_preamble, @@ -202,82 +311,40 @@ execvPe(const char *name, const char *path, char * const *argv, (void)_write(STDERR_FILENO, p, lp); (void)_write(STDERR_FILENO, execvPe_err_trailer, sizeof(execvPe_err_trailer) - 1); + continue; } - bcopy(p, buf, lp); + + memcpy(&buf[0], p, lp); buf[lp] = '/'; - bcopy(name, buf + lp + 1, ln); + memcpy(&buf[lp + 1], name, ln); buf[lp + ln + 1] = '\0'; -retry: (void)_execve(bp, argv, envp); - switch (errno) { - case E2BIG: - goto done; - case ELOOP: - case ENAMETOOLONG: - case ENOENT: - break; - case ENOEXEC: - for (cnt = 0; argv[cnt]; ++cnt) - ; - - /* - * cnt may be 0 above; always allocate at least - * 3 entries so that we can at least fit "sh", bp, and - * the NULL terminator. We can rely on cnt to take into - * account the NULL terminator in all other scenarios, - * as we drop argv[0]. - */ - memp = alloca(MAX(3, cnt + 2) * sizeof(char *)); - if (memp == NULL) { - /* errno = ENOMEM; XXX override ENOEXEC? */ - goto done; - } - if (cnt > 0) { - memp[0] = argv[0]; - memp[1] = bp; - bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); - } else { - memp[0] = "sh"; - memp[1] = bp; - memp[2] = NULL; - } - (void)_execve(_PATH_BSHELL, - __DECONST(char **, memp), envp); - goto done; - case ENOMEM: - goto done; - case ENOTDIR: - break; - case ETXTBSY: - /* - * We used to retry here, but sh(1) doesn't. - */ - goto done; - default: - /* - * EACCES may be for an inaccessible directory or - * a non-executable file. Call stat() to decide - * which. This also handles ambiguities for EFAULT - * and EIO, and undocumented errors like ESTALE. - * We hope that the race for a stat() is unimportant. - */ - save_errno = errno; - if (stat(bp, &sb) != 0) - break; - if (save_errno == EACCES) { - eacces = 1; - continue; - } - errno = save_errno; - goto done; - } + /* + * For terminal conditions we can just return immediately. If + * it was non-terminal, we just need to note if we had an + * EACCES -- execvPe_prog would do a stat(2) and leave us with + * an errno of EACCES only if the file did exist; otherwise it + * would coerce it to an ENOENT because we may not know if a + * file actually existed there or not. + */ + if (execvPe_prog(buf, argv, envp) == -1) + return (-1); + if (errno == EACCES) + eacces = true; } + + /* + * We don't often preserve errors encountering during the PATH search, + * so we override it here. ENOENT would be misleading if we found a + * candidate but couldn't access it, but most of the other conditions + * are either terminal or indicate that nothing was there. + */ if (eacces) errno = EACCES; else errno = ENOENT; -done: + return (-1); } @@ -288,7 +355,7 @@ execvP(const char *name, const char *path, char * const argv[]) } int -execvpe(const char *name, char * const argv[], char * const envp[]) +__libc_execvpe(const char *name, char * const argv[], char * const envp[]) { const char *path; @@ -298,3 +365,5 @@ execvpe(const char *name, char * const argv[], char * const envp[]) return (execvPe(name, path, argv, envp)); } + +__weak_reference(__libc_execvpe, execvpe); diff --git a/lib/libc/gen/fdopendir.c b/lib/libc/gen/fdopendir.c index 67c0766b6d83..9393cbe28f85 100644 --- a/lib/libc/gen/fdopendir.c +++ b/lib/libc/gen/fdopendir.c @@ -30,14 +30,13 @@ */ #include "namespace.h" -#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> +#include <stdbool.h> #include "un-namespace.h" #include "gen-private.h" @@ -49,8 +48,16 @@ DIR * fdopendir(int fd) { + int flags, rc; - if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + flags = _fcntl(fd, F_GETFD, 0); + if (flags == -1) return (NULL); + + if ((flags & FD_CLOEXEC) == 0) { + rc = _fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + if (rc == -1) + return (NULL); + } return (__opendir_common(fd, DTF_HIDEW | DTF_NODUP, true)); } diff --git a/lib/libc/gen/fnmatch.3 b/lib/libc/gen/fnmatch.3 index 804bc968c1ce..7f020fec58e3 100644 --- a/lib/libc/gen/fnmatch.3 +++ b/lib/libc/gen/fnmatch.3 @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 2, 2022 +.Dd April 7, 2025 .Dt FNMATCH 3 .Os .Sh NAME @@ -129,12 +129,8 @@ otherwise, it returns the value .Sh STANDARDS The current implementation of the .Fn fnmatch -function -.Em does not -conform to +function is expected to conform to .St -p1003.2 . -Collating symbol expressions, equivalence class expressions and -character class expressions are not supported. .Sh HISTORY A predecessor to .Fn fnmatch , diff --git a/lib/libc/gen/fnmatch.c b/lib/libc/gen/fnmatch.c index fb1829e69502..1c583a9d23e2 100644 --- a/lib/libc/gen/fnmatch.c +++ b/lib/libc/gen/fnmatch.c @@ -67,7 +67,8 @@ #define RANGE_NOMATCH 0 #define RANGE_ERROR (-1) -static int rangematch(const char *, wchar_t, int, char **, mbstate_t *); +static int rangematch(const char *, wchar_t, const char *, int, char **, + char **, mbstate_t *, mbstate_t *); static int fnmatch1(const char *, const char *, const char *, int, mbstate_t, mbstate_t); @@ -85,7 +86,7 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart, { const char *bt_pattern, *bt_string; mbstate_t bt_patmbs, bt_strmbs; - char *newp; + char *newp, *news; char c; wchar_t pc, sc; size_t pclen, sclen; @@ -151,8 +152,10 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart, * there is no way having it match more characters * can help us, given that we are already here. */ - bt_pattern = pattern, bt_patmbs = patmbs; - bt_string = string, bt_strmbs = strmbs; + bt_pattern = pattern; + bt_patmbs = patmbs; + bt_string = string; + bt_strmbs = strmbs; break; case '[': if (sc == EOS) @@ -164,17 +167,17 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart, ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) goto backtrack; - switch (rangematch(pattern, sc, flags, &newp, - &patmbs)) { + switch (rangematch(pattern, sc, string + sclen, flags, + &newp, &news, &patmbs, &strmbs)) { case RANGE_ERROR: goto norm; case RANGE_MATCH: pattern = newp; + string = news; break; case RANGE_NOMATCH: goto backtrack; } - string += sclen; break; case '\\': if (!(flags & FNM_NOESCAPE)) { @@ -218,8 +221,10 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart, if (sc == '/' && flags & FNM_PATHNAME) return (FNM_NOMATCH); bt_string += sclen; - pattern = bt_pattern, patmbs = bt_patmbs; - string = bt_string, strmbs = bt_strmbs; + pattern = bt_pattern; + patmbs = bt_patmbs; + string = bt_string; + strmbs = bt_strmbs; } break; } @@ -228,15 +233,20 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart, } static int -rangematch(const char *pattern, wchar_t test, int flags, char **newp, - mbstate_t *patmbs) +rangematch(const char *pattern, wchar_t test, const char *string, int flags, + char **newp, char **news, mbstate_t *patmbs, mbstate_t *strmbs) { int negate, ok; wchar_t c, c2; size_t pclen; const char *origpat; struct xlocale_collate *table = - (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; + (struct xlocale_collate *)__get_locale()->components[XLC_COLLATE]; + wchar_t buf[COLLATE_STR_LEN]; /* STR_LEN defined in collate.h */ + const char *cp, *savestring; + int special; + mbstate_t save; + size_t sclen, len; /* * A bracket expression starting with an unquoted circumflex @@ -259,20 +269,132 @@ rangematch(const char *pattern, wchar_t test, int flags, char **newp, ok = 0; origpat = pattern; for (;;) { + c = 0; if (*pattern == ']' && pattern > origpat) { - pattern++; break; } else if (*pattern == '\0') { return (RANGE_ERROR); } else if (*pattern == '/' && (flags & FNM_PATHNAME)) { return (RANGE_NOMATCH); - } else if (*pattern == '\\' && !(flags & FNM_NOESCAPE)) + } else if (*pattern == '\\' && !(flags & FNM_NOESCAPE)) { pattern++; - pclen = mbrtowc(&c, pattern, MB_LEN_MAX, patmbs); - if (pclen == (size_t)-1 || pclen == (size_t)-2) - return (RANGE_NOMATCH); - pattern += pclen; + } else if (*pattern == '[' && + ((special = *(pattern + 1)) == '.' || + special == '=' || special == ':')) { + cp = (pattern += 2); + while ((cp = strchr(cp, special))) { + if (*(cp + 1) == ']') + break; + cp++; + } + if (!cp) + return (RANGE_ERROR); + if (special == '.') { +treat_like_collating_symbol: + len = __collate_collating_symbol(buf, + COLLATE_STR_LEN, pattern, + cp - pattern, patmbs); + if (len == (size_t)-1 || len == 0) + return (RANGE_ERROR); + pattern = cp + 2; + if (len > 1) { + wchar_t *wp, sc; + /* + * No multi-character collation + * symbols as start of range. + */ + if (*(cp + 2) == '-' && + *(cp + 3) != EOS && + *(cp + 3) != ']') + return (RANGE_ERROR); + wp = buf; + if (test != *wp++) + continue; + if (len == 1) { + ok = 1; + break; + } + memcpy(&save, strmbs, sizeof(save)); + savestring = string; + while (--len > 0) { + sclen = mbrtowc(&sc, string, + MB_LEN_MAX, strmbs); + if (sclen == (size_t)-1 || + sclen == (size_t)-2) { + sc = (unsigned char)*string; + sclen = 1; + memset(&strmbs, 0, + sizeof(strmbs)); + } + if (sc != *wp++) { + memcpy(strmbs, &save, + sizeof(save)); + string = savestring; + break; + } + string += sclen; + } + if (len == 0) { + ok = 1; + break; + } + continue; /* no match */ + } + c = *buf; + } else if (special == '=') { + int ec; + memcpy(&save, patmbs, sizeof(save)); + ec = __collate_equiv_class(pattern, + cp - pattern, patmbs); + if (ec < 0) + return (RANGE_ERROR); + if (ec == 0) { + memcpy(patmbs, &save, sizeof(save)); + goto treat_like_collating_symbol; + } + pattern = cp + 2; + /* no equivalence classes as start of range */ + if (*(cp + 2) == '-' && *(cp + 3) != EOS && + *(cp + 3) != ']') + return (RANGE_ERROR); + len = __collate_equiv_match(ec, NULL, 0, test, + string, strlen(string), strmbs, &sclen); + if (len < 0) + return (RANGE_ERROR); + if (len > 0) { + ok = 1; + string += sclen; + break; + } + continue; + } else { /* special == ':' */ + wctype_t charclass; + char name[CHARCLASS_NAME_MAX + 1]; + /* no character classes as start of range */ + if (*(cp + 2) == '-' && *(cp + 3) != EOS && + *(cp + 3) != ']') + return (RANGE_ERROR); + /* assume character class names are ascii */ + if (cp - pattern > CHARCLASS_NAME_MAX) + return (RANGE_ERROR); + strlcpy(name, pattern, cp - pattern + 1); + pattern = cp + 2; + if ((charclass = wctype(name)) == 0) + return (RANGE_ERROR); + if (iswctype(test, charclass)) { + ok = 1; + break; + } + continue; + } + } + if (!c) { + pclen = mbrtowc(&c, pattern, MB_LEN_MAX, patmbs); + if (pclen == (size_t)-1 || pclen == (size_t)-2) + return (RANGE_NOMATCH); + pattern += pclen; + } if (flags & FNM_CASEFOLD) c = towlower(c); @@ -288,6 +410,37 @@ rangematch(const char *pattern, wchar_t test, int flags, char **newp, if (c2 == EOS) return (RANGE_ERROR); + if ((c2 == '[' && (special = *pattern) == '.') || + special == '=' || special == ':') { + + /* + * No equivalence classes or character + * classes as end of range. + */ + if (special == '=' || special == ':') + return (RANGE_ERROR); + cp = ++pattern; + while ((cp = strchr(cp, special))) { + if (*(cp + 1) == ']') + break; + cp++; + } + if (!cp) + return (RANGE_ERROR); + len = __collate_collating_symbol(buf, + COLLATE_STR_LEN, pattern, + cp - pattern, patmbs); + + /* + * No multi-character collation symbols + * as end of range. + */ + if (len != 1) + return (RANGE_ERROR); + pattern = cp + 2; + c2 = *buf; + } + if (flags & FNM_CASEFOLD) c2 = towlower(c2); @@ -295,12 +448,44 @@ rangematch(const char *pattern, wchar_t test, int flags, char **newp, c <= test && test <= c2 : __wcollate_range_cmp(c, test) <= 0 && __wcollate_range_cmp(test, c2) <= 0 - ) + ) { ok = 1; - } else if (c == test) + break; + } + } else if (c == test) { ok = 1; + break; + } } - *newp = (char *)pattern; + /* go to end of bracket expression */ + special = 0; + while (*pattern != ']') { + if (*pattern == 0) + return (RANGE_ERROR); + if (*pattern == special) { + if (*++pattern == ']') { + special = 0; + pattern++; + } + continue; + } + if (!special && *pattern == '[') { + special = *++pattern; + if (special != '.' && special != '=' && special != ':') + special = 0; + else + pattern++; + continue; + } + pclen = mbrtowc(&c, pattern, MB_LEN_MAX, patmbs); + if (pclen == (size_t)-1 || pclen == (size_t)-2) + return (RANGE_NOMATCH); + pattern += pclen; + } + + *newp = (char *)++pattern; + *news = (char *)string; + return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); } diff --git a/lib/libc/gen/fts-compat.c b/lib/libc/gen/fts-compat.c index f87cabf085f7..62a1e0a81f62 100644 --- a/lib/libc/gen/fts-compat.c +++ b/lib/libc/gen/fts-compat.c @@ -44,9 +44,9 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "gen-compat.h" #include "fts-compat.h" #include "un-namespace.h" +#include "gen-compat.h" #include "gen-private.h" diff --git a/lib/libc/gen/fts-compat11.c b/lib/libc/gen/fts-compat11.c index 0351ce5ac690..5abb378f5f08 100644 --- a/lib/libc/gen/fts-compat11.c +++ b/lib/libc/gen/fts-compat11.c @@ -43,9 +43,9 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "gen-compat.h" #include "fts-compat11.h" #include "un-namespace.h" +#include "gen-compat.h" #include "gen-private.h" diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3 index 468b14115ec6..da304e59ee72 100644 --- a/lib/libc/gen/fts.3 +++ b/lib/libc/gen/fts.3 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 12, 2014 +.Dd October 6, 2025 .Dt FTS 3 .Os .Sh NAME @@ -37,6 +37,8 @@ .In fts.h .Ft FTS * .Fn fts_open "char * const *path_argv" "int options" "int (*compar)(const FTSENT * const *, const FTSENT * const *)" +.Ft FTS * +.Fn fts_open_b "char * const *path_argv" "int options" "int (^compar)(const FTSENT * const *, const FTSENT * const *)" .Ft FTSENT * .Fn fts_read "FTS *ftsp" .Ft FTSENT * @@ -59,20 +61,23 @@ functions are provided for traversing file hierarchies. A simple overview is that the .Fn fts_open -function returns a +and +.Fn fts_open_b +functions return a .Dq handle on a file hierarchy, which is then supplied to the other .Nm functions. -The function +The .Fn fts_read -returns a pointer to a structure describing one of the files in the file -hierarchy. -The function +function returns a pointer to a structure describing one of the files +in the file hierarchy. +The .Fn fts_children -returns a pointer to a linked list of structures, each of which describes -one of the files contained in a directory in the hierarchy. +function returns a pointer to a linked list of structures, each of +which describes one of the files contained in a directory in the +hierarchy. In general, directories are visited two distinguishable times; in pre-order (before any of their descendants are visited) and in post-order (after all of their descendants have been visited). @@ -176,6 +181,12 @@ by one of the other values. .It Dv FTS_DNR A directory which cannot be read. +This immediately follows +.Dv FTS_D , +in place of +.Dv FTS_DP , +when the directory could not be entered, or could be entered but not +read. This is an error return, and the .Fa fts_errno field will be set to indicate what caused the error. @@ -186,6 +197,8 @@ or .Ql ..\& which was not specified as a file name to .Fn fts_open +or +.Fn fts_open_b (see .Dv FTS_SEEDOT ) . .It Dv FTS_DP @@ -234,6 +247,8 @@ A path for accessing the file from the current directory. The path for the file relative to the root of the traversal. This path contains the path specified to .Fn fts_open +or +.Fn fts_open_b as a prefix. .It Fa fts_pathlen The length of the string referenced by @@ -362,7 +377,44 @@ The .Fa fts_name field is always .Dv NUL Ns -terminated . -.Sh FTS_OPEN +.Ss Thread Safety +The +.Nm +functions can safely be used in multi-threaded programs provided no +two threads access the same +.Vt FTS +or +.Vt FTSENT +structure simultaneously. +However, unless the +.Dv FTS_NOCHDIR +flag was passed to +.Fn fts_open +or +.Fn fts_open_b , +calls to +.Fn fts_read +and +.Fn fts_children +may change the current working directory, which will affect all +threads. +Conversely, changing the current working directory either during or +between calls to +.Fn fts_read +or +.Fn fts_children +(even in a single-thread program) may cause +.Nm +to malfunction unless the +.Dv FTS_NOCHDIR +flag was passed to +.Fn fts_open +or +.Fn fts_open_b +and all paths in +.Va path_argv +were absolute. +.Ss Fn fts_open The .Fn fts_open function takes a pointer to an array of character pointers naming one @@ -380,12 +432,16 @@ must be specified. The options are selected by .Em or Ns 'ing the following values: -.Bl -tag -width "FTS_PHYSICAL" +.Bl -tag -width "FTS_COMFOLLOWDIR" .It Dv FTS_COMFOLLOW This option causes any symbolic link specified as a root path to be followed immediately whether or not .Dv FTS_LOGICAL is also specified. +.It Dv FTS_COMFOLLOWDIR +This option is similar to +.Dv FTS_COMFOLLOW , +but only follows symbolic links to directories. .It Dv FTS_LOGICAL This option causes the .Nm @@ -441,6 +497,15 @@ field to and leave the contents of the .Fa statp field undefined. +.It Dv FTS_NOSTAT_TYPE +This option is similar to +.Dv FTS_NOSTAT , +but attempts to populate +.Fa fts_info +based on information from the +.Fa d_type +field of +.Vt struct dirent . .It Dv FTS_PHYSICAL This option causes the .Nm @@ -480,10 +545,10 @@ from descending into directories that have a different device number than the file from which the descent began. .El .Pp -The argument -.Fn compar -specifies a user-defined function which may be used to order the traversal -of the hierarchy. +The +.Fa compar +argument points to a user-defined function which may be used to order +the traversal of the hierarchy. It takes two pointers to pointers to .Vt FTSENT @@ -518,7 +583,16 @@ the directory traversal order is in the order listed in .Fa path_argv for the root paths, and in the order listed in the directory for everything else. -.Sh FTS_READ +.Ss Fn fts_open_b +The +.Fn fts_open_b +function is identical to +.Fn fts_open +except that it takes a block pointer instead of a function pointer. +The block is copied before +.Fn fts_open_b +returns, so the original can safely go out of scope or be released. +.Ss Fn fts_read The .Fn fts_read function returns a pointer to an @@ -552,6 +626,15 @@ structure is returned, and .Va errno may or may not have been set (see .Fa fts_info ) . +Note that +.Fn fts_read +will not set +.Va errno +to 0 if called again with the same +.Fa ftsp +argument after the +.Dv FTS_STOP +flag has been set or the end of the stream has been reached. .Pp The .Vt FTSENT @@ -566,10 +649,10 @@ directory, in which case they will not be overwritten until after a call to .Fn fts_read after the .Vt FTSENT -structure has been returned by the function +structure has been returned by the .Fn fts_read -in post-order. -.Sh FTS_CHILDREN +function in post-order. +.Ss Fn fts_children The .Fn fts_children function returns a pointer to an @@ -593,9 +676,13 @@ As a special case, if has not yet been called for a hierarchy, .Fn fts_children will return a pointer to the files in the logical directory specified to -.Fn fts_open , +.Fn fts_open +or +.Fn fts_open_b , i.e., the arguments specified to -.Fn fts_open . +.Fn fts_open +or +.Fn fts_open_b . Otherwise, if the .Vt FTSENT structure most recently returned by @@ -639,11 +726,11 @@ and .Fa fts_namelen fields. .El -.Sh FTS_SET -The function +.Ss Fn fts_set +The .Fn fts_set -allows the user application to determine further processing for the -file +function allows the user application to determine further processing +for the file .Fa f of the stream .Fa ftsp . @@ -709,43 +796,156 @@ The file may be one of those most recently returned by either or .Fn fts_read . .El -.Sh FTS_CLOSE +.Ss Fn fts_set_clientptr , Fn fts_get_clientptr +The +.Fn fts_set_clientptr +function sets the client data pointer for the stream +.Fa ftsp +to +.Fa clientdata . +The +.Fn fts_get_clientptr +function returns the client data pointer associated with +.Fa ftsp . +This can be used to pass per-stream data to the comparison function. +.Pp +For performance reasons, +.Fn fts_get_clientptr +may be shadowed by a preprocessor macro. +.Ss Fn fts_get_stream +The +.Fn fts_get_stream +function returns the +.Nm +stream associated with the file entry +.Fa f . +A typical use for this would be for a comparison function to first call +.Fn fts_get_stream +on one of its arguments, then call +.Fn fts_get_clientptr +to obtain the client data pointer, which in turn points to information +necessary to correctly order the two entries. +.Pp +For performance reasons, +.Fn fts_get_stream +may be shadowed by a preprocessor macro. +.Ss Fn fts_close The .Fn fts_close function closes a file hierarchy stream .Fa ftsp and restores the current directory to the directory from which .Fn fts_open +or +.Fn fts_open_b was called to open .Fa ftsp . +.Sh RETURN VALUES +The +.Fn fts_open +and +.Fn fts_open_b +functions return a pointer to the new +.Nm +stream on success and +.Dv NULL +on failure. +.Pp +The +.Fn fts_read +function returns a pointer to the next file entry on success, or if an +error occurs that relates specifically to that file entry. +On reaching the end of the file hierarchy, it returns +.Dv NULL +and sets the external variable +.Va errno +to 0. +On failure, it returns +.Dv NULL +and sets +.Va errno +to an appropriate non-zero value. +If called again after the +.Dv FTS_STOP +flag has been set or the end of the stream has been reached, +.Fn fts_read +returns +.Dv NULL +and leaves +.Va errno +untouched. +.Pp +The +.Fn fts_children +function returns a pointer to a linked list of file entries on +success. +On reaching the end of the file hierarchy, it returns +.Dv NULL +and sets the external variable +.Va errno +to 0. +On failure, it returns +.Dv NULL +and sets +.Va errno +to an appropriate non-zero value. +.Pp +The +.Fn fts_set +function returns 0 on success and \-1 if its +.Fa instr +argument is invalid. +.Pp +The +.Fn fts_get_clientptr +function returns the client data pointer associated with its argument, +or +.Dv NULL +if none has been set. +.Pp +The +.Fn fts_get_stream +function returns a pointer to the +.Nm +stream associated with its argument. +.Pp The .Fn fts_close function returns 0 on success, and \-1 if an error occurs. .Sh ERRORS -The function +The .Fn fts_open -may fail and set +and +.Fn fts_open_b +functions may fail and set .Va errno for any of the errors specified for the library functions .Xr open 2 and .Xr malloc 3 . +The +.Fn fts_open_b +function may also fail and set +.Va errno +to +.Dv ENOSYS +if the blocks runtime is missing. .Pp -The function +The .Fn fts_close -may fail and set +function may fail and set .Va errno for any of the errors specified for the library functions .Xr chdir 2 and .Xr close 2 . .Pp -The functions +The .Fn fts_read and .Fn fts_children -may fail and set +functions may fail and set .Va errno for any of the errors specified for the library functions .Xr chdir 2 , @@ -755,17 +955,17 @@ for any of the errors specified for the library functions and .Xr stat 2 . .Pp -In addition, +In addition, the .Fn fts_children , -.Fn fts_open +.Fn fts_open , and .Fn fts_set -may fail and set +functions may fail and set .Va errno as follows: .Bl -tag -width Er .It Bq Er EINVAL -The options were invalid, or the list were empty. +The options were invalid, or the list was empty. .El .Sh SEE ALSO .Xr find 1 , @@ -788,6 +988,13 @@ functions were introduced in principally to provide for alternative interfaces to the .Nm functionality using different data structures. +Blocks support and the +.Dv FTS_COMFOLLOWDIR +and +.Dv FTS_NOSTAT +options were added in +.Fx 15.0 +based on similar functionality in macOS. .Sh BUGS The .Fn fts_open diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index 0c26c91feb84..4aa386d777cd 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -48,6 +48,19 @@ #include "gen-private.h" +#ifdef __BLOCKS__ +#include <Block.h> +#else +#include "block_abi.h" +typedef DECLARE_BLOCK(int, fts_block, + const FTSENT * const *, const FTSENT * const *); +void qsort_b(void *, size_t, size_t, fts_block); +#endif /* __BLOCKS__ */ +/* only present if linked with blocks runtime */ +void *_Block_copy(const void *) __weak_symbol; +void _Block_release(const void *) __weak_symbol; +extern void *_NSConcreteGlobalBlock[] __weak_symbol; + static FTSENT *fts_alloc(FTS *, char *, size_t); static FTSENT *fts_build(FTS *, int); static void fts_lfree(FTSENT *); @@ -93,7 +106,6 @@ struct _fts_private { * This assumption only holds for UFS-like filesystems that implement * links and directories this way, so we must punt for others. */ - static const char *ufslike_filesystems[] = { "ufs", "zfs", @@ -102,39 +114,21 @@ static const char *ufslike_filesystems[] = { 0 }; -FTS * -fts_open(char * const *argv, int options, - int (*compar)(const FTSENT * const *, const FTSENT * const *)) +static FTS * +__fts_open(FTS *sp, char * const *argv) { - struct _fts_private *priv; - FTS *sp; FTSENT *p, *root; FTSENT *parent, *tmp; size_t len, nitems; - /* Options check. */ - if (options & ~FTS_OPTIONMASK) { - errno = EINVAL; - return (NULL); - } - - /* fts_open() requires at least one path */ - if (*argv == NULL) { - errno = EINVAL; - return (NULL); - } - - /* Allocate/initialize the stream. */ - if ((priv = calloc(1, sizeof(*priv))) == NULL) - return (NULL); - sp = &priv->ftsp_fts; - sp->fts_compar = compar; - sp->fts_options = options; - /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ if (ISSET(FTS_LOGICAL)) SET(FTS_NOCHDIR); + /* NOSTAT_TYPE implies NOSTAT */ + if (ISSET(FTS_NOSTAT_TYPE)) + SET(FTS_NOSTAT); + /* * Start out with 1K of path space, and enough, in any case, * to hold the user's paths. @@ -158,7 +152,9 @@ fts_open(char * const *argv, int options, p->fts_level = FTS_ROOTLEVEL; p->fts_parent = parent; p->fts_accpath = p->fts_name; - p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1); + p->fts_info = fts_stat(sp, p, + ISSET(FTS_COMFOLLOWDIR) ? -1 : ISSET(FTS_COMFOLLOW), + -1); /* Command-line "." and ".." are real directories. */ if (p->fts_info == FTS_DOT) @@ -168,7 +164,7 @@ fts_open(char * const *argv, int options, * If comparison routine supplied, traverse in sorted * order; otherwise traverse in the order specified. */ - if (compar) { + if (sp->fts_compar) { p->fts_link = root; root = p; } else { @@ -181,7 +177,7 @@ fts_open(char * const *argv, int options, } } } - if (compar && nitems > 1) + if (sp->fts_compar && nitems > 1) root = fts_sort(sp, root, nitems); /* @@ -214,6 +210,97 @@ mem1: free(sp); return (NULL); } +FTS * +fts_open(char * const *argv, int options, + int (*compar)(const FTSENT * const *, const FTSENT * const *)) +{ + struct _fts_private *priv; + FTS *sp; + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* fts_open() requires at least one path */ + if (*argv == NULL) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream. */ + if ((priv = calloc(1, sizeof(*priv))) == NULL) + return (NULL); + sp = &priv->ftsp_fts; + sp->fts_compar = compar; + sp->fts_options = options; + + return (__fts_open(sp, argv)); +} + +#ifdef __BLOCKS__ +FTS * +fts_open_b(char * const *argv, int options, + int (^compar)(const FTSENT * const *, const FTSENT * const *)) +#else +FTS * +fts_open_b(char * const *argv, int options, fts_block compar) +#endif /* __BLOCKS__ */ +{ + struct _fts_private *priv; + FTS *sp; + + /* No blocks, no problems. */ + if (compar == NULL) + return (fts_open(argv, options, NULL)); + + /* Avoid segfault if blocks runtime is missing. */ + if (_Block_copy == NULL) { + errno = ENOSYS; + return (NULL); + } + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* fts_open() requires at least one path */ + if (*argv == NULL) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream. */ + if ((priv = calloc(1, sizeof(*priv))) == NULL) + return (NULL); + sp = &priv->ftsp_fts; +#ifdef __BLOCKS__ + compar = Block_copy(compar); +#else + if (compar->isa != &_NSConcreteGlobalBlock) + compar = _Block_copy(compar); +#endif /* __BLOCKS__ */ + if (compar == NULL) { + free(priv); + return (NULL); + } + sp->fts_compar_b = compar; + sp->fts_options = options | FTS_COMPAR_B; + + if ((sp = __fts_open(sp, argv)) == NULL) { +#ifdef __BLOCKS__ + Block_release(compar); +#else + if (compar->isa != &_NSConcreteGlobalBlock) + _Block_release(compar); +#endif /* __BLOCKS__ */ + } + return (sp); +} + static void fts_load(FTS *sp, FTSENT *p) { @@ -265,6 +352,17 @@ fts_close(FTS *sp) free(sp->fts_array); free(sp->fts_path); + /* Free up any block pointer. */ + if (ISSET(FTS_COMPAR_B) && sp->fts_compar_b != NULL) { +#ifdef __BLOCKS__ + Block_release(sp->fts_compar_b); +#else + if (((fts_block)(sp->fts_compar_b))->isa != + &_NSConcreteGlobalBlock) + _Block_release(sp->fts_compar_b); +#endif /* __BLOCKS__ */ + } + /* Return to original directory, save errno if necessary. */ if (!ISSET(FTS_NOCHDIR)) { saved_errno = fchdir(sp->fts_rfd) ? errno : 0; @@ -580,7 +678,6 @@ fts_children(FTS *sp, int instr) void * (fts_get_clientptr)(FTS *sp) { - return (fts_get_clientptr(sp)); } @@ -597,7 +694,6 @@ FTS * void fts_set_clientptr(FTS *sp, void *clientptr) { - sp->fts_clientptr = clientptr; } @@ -650,14 +746,10 @@ fts_build(FTS *sp, int type) * Open the directory for reading. If this fails, we're done. * If being called from fts_read, set the fts_info field. */ -#ifdef FTS_WHITEOUT if (ISSET(FTS_WHITEOUT)) oflag = DTF_NODUP; else oflag = DTF_HIDEW | DTF_NODUP; -#else -#define __opendir2(path, flag) opendir(path) -#endif if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { if (type == BREAD) { cur->fts_info = FTS_DNR; @@ -784,10 +876,8 @@ mem1: saved_errno = errno; p->fts_parent = sp->fts_cur; p->fts_pathlen = len + dnamlen; -#ifdef FTS_WHITEOUT if (dp->d_type == DT_WHT) p->fts_flags |= FTS_ISW; -#endif if (cderrno) { if (nlinks) { @@ -796,12 +886,8 @@ mem1: saved_errno = errno; } else p->fts_info = FTS_NSOK; p->fts_accpath = cur->fts_accpath; - } else if (nlinks == 0 -#ifdef DT_DIR - || (nostat && - dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) -#endif - ) { + } else if (nlinks == 0 || (nostat && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)) { p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; p->fts_info = FTS_NSOK; @@ -821,6 +907,25 @@ mem1: saved_errno = errno; p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) --nlinks; } + if (p->fts_info == FTS_NSOK && ISSET(FTS_NOSTAT_TYPE)) { + switch (dp->d_type) { + case DT_FIFO: + case DT_CHR: + case DT_BLK: + case DT_SOCK: + p->fts_info = FTS_DEFAULT; + break; + case DT_REG: + p->fts_info = FTS_F; + break; + case DT_LNK: + p->fts_info = FTS_SL; + break; + case DT_WHT: + p->fts_info = FTS_W; + break; + } + } /* We walk in directory order so "ls -f" doesn't get upset. */ p->fts_link = NULL; @@ -897,7 +1002,7 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) dev_t dev; ino_t ino; struct stat *sbp, sb; - int saved_errno; + int ret, saved_errno; const char *path; if (dfd == -1) { @@ -910,7 +1015,6 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) /* If user needs stat info, stat buffer already allocated. */ sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; -#ifdef FTS_WHITEOUT /* Check for whiteout. */ if (p->fts_flags & FTS_ISW) { if (sbp != &sb) { @@ -919,22 +1023,27 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) } return (FTS_W); } -#endif /* - * If doing a logical walk, or application requested FTS_FOLLOW, do - * a stat(2). If that fails, check for a non-existent symlink. If - * fail, set the errno from the stat call. + * If doing a logical walk, or caller requested FTS_COMFOLLOW, do + * a full stat(2). If that fails, do an lstat(2) to check for a + * non-existent symlink. If that fails, set the errno from the + * stat(2) call. + * + * As a special case, if stat(2) succeeded but the target is not a + * directory and follow is negative (indicating FTS_COMFOLLOWDIR + * rather than FTS_COMFOLLOW), we also revert to lstat(2). */ if (ISSET(FTS_LOGICAL) || follow) { - if (fstatat(dfd, path, sbp, 0)) { + if ((ret = fstatat(dfd, path, sbp, 0)) != 0 || + (follow < 0 && !S_ISDIR(sbp->st_mode))) { saved_errno = errno; if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { p->fts_errno = saved_errno; goto err; } errno = 0; - if (S_ISLNK(sbp->st_mode)) + if (ret != 0 && S_ISLNK(sbp->st_mode)) return (FTS_SLNONE); } } else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { @@ -979,21 +1088,6 @@ err: memset(sbp, 0, sizeof(struct stat)); return (FTS_DEFAULT); } -/* - * The comparison function takes pointers to pointers to FTSENT structures. - * Qsort wants a comparison function that takes pointers to void. - * (Both with appropriate levels of const-poisoning, of course!) - * Use a trampoline function to deal with the difference. - */ -static int -fts_compar(const void *a, const void *b) -{ - FTS *parent; - - parent = (*(const FTSENT * const *)a)->fts_fts; - return (*parent->fts_compar)(a, b); -} - static FTSENT * fts_sort(FTS *sp, FTSENT *head, size_t nitems) { @@ -1016,7 +1110,18 @@ fts_sort(FTS *sp, FTSENT *head, size_t nitems) } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; - qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar); + if (ISSET(FTS_COMPAR_B)) { +#ifdef __BLOCKS__ + qsort_b(sp->fts_array, nitems, sizeof(FTSENT *), + (int (^)(const void *, const void *))sp->fts_compar_b); +#else + qsort_b(sp->fts_array, nitems, sizeof(FTSENT *), + sp->fts_compar_b); +#endif /* __BLOCKS__ */ + } else { + qsort(sp->fts_array, nitems, sizeof(FTSENT *), + (int (*)(const void *, const void *))sp->fts_compar); + } for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; diff --git a/lib/libc/gen/gen-compat.h b/lib/libc/gen/gen-compat.h index 08e80ede6b6e..19b9addb4321 100644 --- a/lib/libc/gen/gen-compat.h +++ b/lib/libc/gen/gen-compat.h @@ -40,16 +40,50 @@ struct freebsd11_statfs; struct freebsd11_dirent *freebsd11_readdir(DIR *); int freebsd11_readdir_r(DIR *, struct freebsd11_dirent *, struct freebsd11_dirent **); -int freebsd11_stat(const char *, struct freebsd11_stat *); -int freebsd11_lstat(const char *, struct freebsd11_stat *); -int freebsd11_fstat(int, struct freebsd11_stat *); -int freebsd11_fstatat(int, const char *, struct freebsd11_stat *, int); -int freebsd11_statfs(const char *, struct freebsd11_statfs *); -int freebsd11_getfsstat(struct freebsd11_statfs *, long, int); int freebsd11_getmntinfo(struct freebsd11_statfs **, int); char *freebsd11_devname(__uint32_t dev, __mode_t type); -char *freebsd11_devname_r(__uint32_t dev, __mode_t type, char *buf, int len); +char *freebsd11_devname_r(__uint32_t dev, __mode_t type, char *buf, + int len); + +/* + * We want freebsd11_fstat in C source to result in resolution to + * - fstat@FBSD_1.0 for libc.so (but we do not need the _definition_ + * of this fstat, it is provided by libsys.so which we want to use). + * - freebsd11_fstat for libc.a (since if we make it fstat@FBSD_1.0 + * for libc.a, then final linkage into static object ignores version + * and would reference fstat, which is the current syscall, not the + * compat syscall). libc.a provides the freebsd11_fstat implementation. + * Note that freebsd11_fstat from libc.a is not used for anything, but + * we make it correct nonetheless, just in case it would. + * This is arranged by COMPAT_SYSCALL, and libc can just use freebsd11_fstat. + */ +#ifdef PIC +#define COMPAT_SYSCALL(rtype, fun, args, sym, ver) \ + rtype fun args; __sym_compat(sym, fun, ver); +#else +#define COMPAT_SYSCALL(rtype, fun, args, sym, ver) \ + rtype fun args; +#endif + +COMPAT_SYSCALL(int, freebsd11_stat, (const char *, struct freebsd11_stat *), + stat, FBSD_1.0); +COMPAT_SYSCALL(int, freebsd11_lstat, (const char *, struct freebsd11_stat *), + lstat, FBSD_1.0); +COMPAT_SYSCALL(int, freebsd11_fstat, (int, struct freebsd11_stat *), + fstat, FBSD_1.0); +COMPAT_SYSCALL(int, freebsd11_fstatat, (int, const char *, + struct freebsd11_stat *, int), fstatat, FBSD_1.1); + +COMPAT_SYSCALL(int, freebsd11_statfs, (const char *, + struct freebsd11_statfs *), statfs, FBSD_1.0); +COMPAT_SYSCALL(int, freebsd11_getfsstat, (struct freebsd11_statfs *, long, + int), getfsstat, FBSD_1.0); + +COMPAT_SYSCALL(int, freebsd14_setgroups, (int gidsize, const __gid_t *gidset), + setgroups, FBSD_1.0); + +#undef COMPAT_SYSCALL #endif /* _GEN_COMPAT_H_ */ diff --git a/lib/libc/gen/gen-private.h b/lib/libc/gen/gen-private.h index 3792a61ff942..b6749b3435cd 100644 --- a/lib/libc/gen/gen-private.h +++ b/lib/libc/gen/gen-private.h @@ -43,8 +43,8 @@ struct pthread_mutex; */ struct _dirdesc { int dd_fd; /* file descriptor associated with directory */ - long dd_loc; /* offset in current buffer */ - long dd_size; /* amount of data returned by getdirentries */ + size_t dd_loc; /* offset in current buffer */ + size_t dd_size; /* amount of data returned by getdirentries */ char *dd_buf; /* data buffer */ int dd_len; /* size of data buffer */ off_t dd_seek; /* magic cookie returned by getdirentries */ diff --git a/lib/libc/gen/getgrouplist.3 b/lib/libc/gen/getgrouplist.3 index e9a980f99751..9e05ff7e7a29 100644 --- a/lib/libc/gen/getgrouplist.3 +++ b/lib/libc/gen/getgrouplist.3 @@ -1,5 +1,13 @@ +.\"- +.\" SPDX-License-Identifier: BSD-3-Clause +.\" .\" Copyright (c) 1991, 1993 .\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 2025 The FreeBSD Foundation +.\" +.\" Portions of this documentation were written by Olivier Certner +.\" <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD +.\" Foundation. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -25,12 +33,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 26, 2014 +.Dd October 9, 2025 .Dt GETGROUPLIST 3 .Os .Sh NAME .Nm getgrouplist -.Nd calculate group access list +.Nd produce a user's effective group list .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -40,36 +48,44 @@ .Sh DESCRIPTION The .Fn getgrouplist -function reads through the group file and calculates -the group access list for the user specified in -.Fa name . -The +function retrieves from the group database the supplementary groups for the user +specified in +.Fa name +and returns the effective group list, whose first group is the value of +.Fa basegid +and the others are the supplementary groups. .Fa basegid -is automatically included in the groups list. -Typically this value is given as -the group number from the password file. +typically is the user's initial numerical group ID from the password database. .Pp -The resulting group list is returned in the array pointed to by +The effective group list is returned in the array pointed to by .Fa groups . -The caller specifies the size of the +The caller specifies the length of the .Fa groups array in the integer pointed to by -.Fa ngroups ; -the actual number of groups found is returned in +.Fa ngroups . +The number of groups of the effective group list, which may be greater than the +.Fa groups +array's length, is returned through .Fa ngroups . .Sh RETURN VALUES The .Fn getgrouplist -function -returns 0 on success and \-1 if the size of the group list is too small to -hold all the user's groups. -Here, the group array will be filled with as many groups as will fit. +function returns 0 on success and \-1 if the length of the group list is too +small to hold all the user's groups. +In the latter case, the +.Fa groups +array is filled with as many groups as possible from the start of the effective +group list, and the length pointed to by +.Fa ngroups +is set to the full length of the latter, thus to a value strictly greater than +before the call. .Sh FILES .Bl -tag -width /etc/group -compact .It Pa /etc/group group membership list .El .Sh SEE ALSO +.Xr setcred 2 , .Xr setgroups 2 , .Xr initgroups 3 .Sh HISTORY diff --git a/lib/libc/gen/getgrouplist.c b/lib/libc/gen/getgrouplist.c index 5bd06bc5121f..9c57b7031336 100644 --- a/lib/libc/gen/getgrouplist.c +++ b/lib/libc/gen/getgrouplist.c @@ -29,13 +29,8 @@ * SUCH DAMAGE. */ -/* - * get credential - */ #include <sys/types.h> -#include <grp.h> -#include <string.h> #include <unistd.h> #include <ssp/ssp.h> @@ -46,4 +41,3 @@ __ssp_real(getgrouplist)(const char *uname, gid_t agroup, gid_t *groups, int *gr { return __getgroupmembership(uname, agroup, groups, *grpcnt, grpcnt); } - diff --git a/lib/libc/gen/glob.3 b/lib/libc/gen/glob.3 index 006e8decb3db..839d98ce02b5 100644 --- a/lib/libc/gen/glob.3 +++ b/lib/libc/gen/glob.3 @@ -27,11 +27,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 20, 2011 +.Dd June 23, 2025 .Dt GLOB 3 .Os .Sh NAME .Nm glob , +.Nm glob_b , .Nm globfree .Nd generate pathnames matching a pattern .Sh LIBRARY @@ -39,7 +40,9 @@ .Sh SYNOPSIS .In glob.h .Ft int -.Fn glob "const char * restrict pattern" "int flags" "int (*errfunc)(const char *, int)" "glob_t * restrict pglob" +.Fn glob "const char * restrict pattern" "int flags" "int (*errfunc)(const char *epath, int errno)" "glob_t * restrict pglob" +.Ft int +.Fn glob_b "const char * restrict pattern" "int flags" "int (^errblk)(const char *epath, int errno)" "glob_t * restrict pglob" .Ft void .Fn globfree "glob_t *pglob" .Sh DESCRIPTION @@ -272,10 +275,24 @@ is .Pf non- Dv NULL , .Fn glob calls -.Fa \*(lp*errfunc\*(rp Ns ( Fa path , errno ) , +.Fa \*(lp*errfunc\*(rp Ns ( Fa path , errno ) . +This may be unintuitive: a pattern like +.Ql */Makefile +will try to +.Xr stat 2 +.Ql foo/Makefile +even if +.Ql foo +is not a directory, resulting in a +call to +.Fa errfunc . +The error routine can suppress this action by testing for +.Er ENOENT +and +.Er ENOTDIR ; however, the .Dv GLOB_ERR -flag will cause an immediate +flag will still cause an immediate return when this happens. .Pp If @@ -307,16 +324,27 @@ or returns zero, the error is ignored. .Pp The +.Fn glob_b +function is like +.Fn glob +except that the error callback is a block pointer instead of a function +pointer. +.Pp +The .Fn globfree function frees any space associated with .Fa pglob from a previous call(s) to -.Fn glob . +.Fn glob +or +.Fn glob_b . .Sh RETURN VALUES On successful completion, .Fn glob -returns zero. -In addition the fields of +and +.Fn glob_b +return zero. +In addition, the fields of .Fa pglob contain the values described below: .Bl -tag -width GLOB_NOCHECK @@ -324,12 +352,16 @@ contain the values described below: contains the total number of matched pathnames so far. This includes other matches from previous invocations of .Fn glob +or +.Fn glob_b if .Dv GLOB_APPEND was specified. .It Fa gl_matchc contains the number of matched pathnames in the current invocation of -.Fn glob . +.Fn glob +or +.Fn glob_b . .It Fa gl_flags contains a copy of the .Fa flags @@ -352,6 +384,8 @@ are undefined. .Pp If .Fn glob +or +.Fn glob_b terminates due to an error, it sets errno and returns one of the following non-zero constants, which are defined in the include file @@ -397,6 +431,14 @@ g.gl_pathv[0] = "ls"; g.gl_pathv[1] = "-l"; execvp("ls", g.gl_pathv); .Ed +.Sh CAVEATS +The +.Fn glob +and +.Fn glob_b +functions +will not match filenames that begin with a period +unless this is specifically requested (e.g., by ".*"). .Sh SEE ALSO .Xr sh 1 , .Xr fnmatch 3 , @@ -435,6 +477,10 @@ and .Fn globfree functions first appeared in .Bx 4.4 . +The +.Fn glob_b +function first appeared in +.Fx 15.0 . .Sh BUGS Patterns longer than .Dv MAXPATHLEN @@ -442,7 +488,9 @@ may cause unchecked errors. .Pp The .Fn glob -argument +and +.Fn glob_b +functions may fail and set errno for any of the errors specified for the library routines .Xr stat 2 , diff --git a/lib/libc/gen/glob.c b/lib/libc/gen/glob.c index 7a988196549a..f7da5f0cda09 100644 --- a/lib/libc/gen/glob.c +++ b/lib/libc/gen/glob.c @@ -88,8 +88,11 @@ #include <unistd.h> #include <wchar.h> +#include "block_abi.h" #include "collate.h" +typedef DECLARE_BLOCK(int, glob_b_block, const char*, int); + /* * glob(3) expansion limits. Stop the expansion if any of these limits * is reached. This caps the runtime in the face of DoS attacks. See @@ -104,8 +107,8 @@ struct glob_limit { size_t l_brace_cnt; size_t l_path_lim; - size_t l_readdir_cnt; - size_t l_stat_cnt; + size_t l_readdir_cnt; + size_t l_stat_cnt; size_t l_string_cnt; }; @@ -179,9 +182,8 @@ static int err_aborted(glob_t *, int, char *); static void qprintf(const char *, Char *); #endif -int -glob(const char * __restrict pattern, int flags, - int (*errfunc)(const char *, int), glob_t * __restrict pglob) +static int +__glob(const char *pattern, glob_t *pglob) { struct glob_limit limit = { 0, 0, 0, 0, 0 }; const char *patnext; @@ -192,25 +194,23 @@ glob(const char * __restrict pattern, int flags, int too_long; patnext = pattern; - if (!(flags & GLOB_APPEND)) { + if (!(pglob->gl_flags & GLOB_APPEND)) { pglob->gl_pathc = 0; pglob->gl_pathv = NULL; - if (!(flags & GLOB_DOOFFS)) + if (!(pglob->gl_flags & GLOB_DOOFFS)) pglob->gl_offs = 0; } - if (flags & GLOB_LIMIT) { + if (pglob->gl_flags & GLOB_LIMIT) { limit.l_path_lim = pglob->gl_matchc; if (limit.l_path_lim == 0) limit.l_path_lim = GLOB_LIMIT_PATH; } - pglob->gl_flags = flags & ~GLOB_MAGCHAR; - pglob->gl_errfunc = errfunc; pglob->gl_matchc = 0; bufnext = patbuf; bufend = bufnext + MAXPATHLEN - 1; too_long = 1; - if (flags & GLOB_NOESCAPE) { + if (pglob->gl_flags & GLOB_NOESCAPE) { memset(&mbs, 0, sizeof(mbs)); while (bufnext <= bufend) { clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); @@ -250,15 +250,45 @@ glob(const char * __restrict pattern, int flags, return (err_nomatch(pglob, &limit, pattern)); *bufnext = EOS; - if (flags & GLOB_BRACE) + if (pglob->gl_flags & GLOB_BRACE) return (globexp0(patbuf, pglob, &limit, pattern)); else return (glob0(patbuf, pglob, &limit, pattern)); } +int +glob(const char * __restrict pattern, int flags, + int (*errfunc)(const char *, int), glob_t * __restrict pglob) +{ + int rv; + + pglob->gl_flags = flags & ~(GLOB_MAGCHAR | _GLOB_ERR_BLOCK); + pglob->gl_errfunc = errfunc; + rv = __glob(pattern, pglob); + pglob->gl_errfunc = NULL; + + return (rv); +} + +int +glob_b(const char * __restrict pattern, int flags, + glob_b_block block, glob_t * __restrict pglob) +{ + int rv; + + pglob->gl_flags = flags & ~GLOB_MAGCHAR; + pglob->gl_flags |= _GLOB_ERR_BLOCK; + pglob->gl_errblk = block; + rv = __glob(pattern, pglob); + pglob->gl_errblk = NULL; + + return (rv); +} + static int globexp0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, - const char *origpat) { + const char *origpat) +{ int rv; size_t oldpathc; @@ -724,7 +754,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pathend_last, return (GLOB_NOSPACE); } *pathend = EOS; - if (pglob->gl_errfunc != NULL && + if ((pglob->gl_errfunc != NULL || pglob->gl_errblk != NULL) && g_Ctoc(pathbuf, buf, sizeof(buf))) { errno = E2BIG; return (GLOB_NOSPACE); @@ -1085,10 +1115,21 @@ err_nomatch(glob_t *pglob, struct glob_limit *limit, const char *origpat) { } static int -err_aborted(glob_t *pglob, int err, char *buf) { - if ((pglob->gl_errfunc != NULL && pglob->gl_errfunc(buf, err)) || - (pglob->gl_flags & GLOB_ERR)) +err_aborted(glob_t *pglob, int err, char *buf) +{ + int rv = 0; + + if ((pglob->gl_flags & _GLOB_ERR_BLOCK) != 0) { + if (pglob->gl_errblk != NULL) + rv = CALL_BLOCK((glob_b_block)pglob->gl_errblk, buf, + errno); + } else if (pglob->gl_errfunc != NULL) { + rv = pglob->gl_errfunc(buf, errno); + } + /* GLOB_ERR is allowed to override the error callback function. */ + if (rv != 0 || pglob->gl_flags & GLOB_ERR) { return (GLOB_ABORTED); + } return (0); } diff --git a/lib/libc/gen/initgroups.3 b/lib/libc/gen/initgroups.3 index 03bd07494fc9..74133e7d7048 100644 --- a/lib/libc/gen/initgroups.3 +++ b/lib/libc/gen/initgroups.3 @@ -1,5 +1,13 @@ +.\"- +.\" SPDX-License-Identifier: BSD-3-Clause +.\" .\" Copyright (c) 1983, 1991, 1993 .\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 2025 The FreeBSD Foundation +.\" +.\" Portions of this documentation were written by Olivier Certner +.\" <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD +.\" Foundation. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -25,12 +33,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 26, 2014 +.Dd October 9, 2025 .Dt INITGROUPS 3 .Os .Sh NAME .Nm initgroups -.Nd initialize group access list +.Nd initialize supplementary groups as per the group database .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -40,19 +48,18 @@ .Sh DESCRIPTION The .Fn initgroups -function -uses the -.Xr getgrouplist 3 -function to calculate the group access list for the user -specified in +function initializes the current process' supplementary groups as prescribed by +its arguments and the system's group database. +.Pp +It first uses the +.Fn getgrouplist +function to compute a list of groups containing the passed +.Fa basegid , +which typically is the user's initial numerical group ID from the password +database, and the supplementary groups in the group database for the user named .Fa name . -This group list is then setup for the current process using -.Xr setgroups 2 . -The -.Fa basegid -is automatically included in the groups list. -Typically this value is given as -the group number from the password file. +It then installs this list as the current process' supplementary groups using +.Fn setgroups . .Sh RETURN VALUES .Rv -std initgroups .Sh ERRORS @@ -60,9 +67,9 @@ The .Fn initgroups function may fail and set .Va errno -for any of the errors specified for the library function -.Xr setgroups 2 . -It may also return: +to any of the errors specified for the +.Xr setgroups 2 +system call, or to: .Bl -tag -width Er .It Bq Er ENOMEM The @@ -77,3 +84,67 @@ The .Fn initgroups function appeared in .Bx 4.2 . +.Pp +The +.Fn initgroups +function changed semantics in +.Fx 15 , +following that of +.Xr setgroups 2 +in the same release. +Before that, it would also set the effective group ID to +.Fa basegid , +and would not include the latter in the supplementary groups except before +.Fx 8 . +Its current behavior in these respects is known to be compatible with that of +the following systems up to the specified versions that are current at time of +this writing: +.Bl -dash -width "-" -compact +.It +Linux (up to 6.6) with the GNU libc (up to 2.42) +.It +.Nx 1.1 and greater (up to 10) +.It +.Ox (up to 7.7) +.It +Systems based on illumos (up to August 2025 sources) +.El +.Sh SECURITY CONSIDERATIONS +As +.Fa basegid +is typically the user's initial numerical group ID, to which the current +process' effective group ID is generally initialized, processes using functions +to change their effective group ID +.Pq via Xr setgid 2 or similar +or that are spawned from executables with the set-group-ID mode bit set will not +be able to relinquish the access rights deriving from being a member of +.Fa basegid , +as these functions do not change the supplementary groups. +.Pp +This behavior is generally desirable in order to paper over the difference of +treatment between the effective group and supplementary ones in this situation, +as they are all in the end indiscriminately used in traditional UNIX +discretionary access checks. +It blends well with the practice of allocating each user its own private group, +as processes launched from a set-group-ID executable keep the same user and +consistently stay also in the same user's group. +Finally, it was also chosen for compatibility with other systems +.Po +see the +.Sx HISTORY +section +.Pc . +.Pp +This convention of including +.Fa basegid +in the supplementary groups is however only enforced by the +.Fn initgroups +function, and not by the +.Xr setgroups 2 +system call, so applications expressly wanting to include in the supplementary +groups only those specified by the group database can themselves call +.Fn getgrouplist +and then +.Fn setgroups +on the result with the first element skipped +.Pq see Xr getgrouplist 3 . diff --git a/lib/libc/gen/initgroups.c b/lib/libc/gen/initgroups.c index b6697dd7ed8f..a1a7d92250e2 100644 --- a/lib/libc/gen/initgroups.c +++ b/lib/libc/gen/initgroups.c @@ -3,6 +3,11 @@ * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. + * Copyright (c) 2025 The FreeBSD Foundation + * + * Portions of this software were developed by Olivier Certner + * <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD + * Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,34 +34,52 @@ * SUCH DAMAGE. */ -#include <sys/param.h> +/* For __sym_compat(). */ +#include <sys/cdefs.h> -#include "namespace.h" -#include <err.h> -#include "un-namespace.h" #include <errno.h> -#include <stdio.h> #include <stdlib.h> #include <unistd.h> -int -initgroups(const char *uname, gid_t agroup) +/* For freebsd14_setgroups(). */ +#include "gen-compat.h" + +static int +initgroups_impl(const char *uname, gid_t agroup, + int (*setgroups)(int, const gid_t *)) { - int ngroups, ret; - long ngroups_max; gid_t *groups; + long ngroups_max; + int ngroups, ret; /* - * Provide space for one group more than possible to allow - * setgroups to fail and set errno. + * Provide space for one group more than possible to allow setgroups() + * to fail and set 'errno' in case we get back more than {NGROUPS_MAX} + + * 1 groups. */ ngroups_max = sysconf(_SC_NGROUPS_MAX) + 2; - if ((groups = malloc(sizeof(*groups) * ngroups_max)) == NULL) - return (ENOMEM); + groups = malloc(sizeof(*groups) * ngroups_max); + if (groups == NULL) + return (-1); /* malloc() set 'errno'. */ ngroups = (int)ngroups_max; - getgrouplist(uname, agroup, groups, &ngroups); - ret = setgroups(ngroups, groups); + (void)getgrouplist(uname, agroup, groups, &ngroups); + ret = (*setgroups)(ngroups, groups); + free(groups); - return (ret); + return (ret); /* setgroups() set 'errno'. */ } + +int +initgroups(const char *uname, gid_t agroup) +{ + return (initgroups_impl(uname, agroup, setgroups)); +} + +int +freebsd14_initgroups(const char *uname, gid_t agroup) +{ + return (initgroups_impl(uname, agroup, freebsd14_setgroups)); +} + +__sym_compat(initgroups, freebsd14_initgroups, FBSD_1.0); diff --git a/lib/libc/gen/inotify.c b/lib/libc/gen/inotify.c new file mode 100644 index 000000000000..7ce53aaccd58 --- /dev/null +++ b/lib/libc/gen/inotify.c @@ -0,0 +1,48 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Klara, Inc. + */ + +#include "namespace.h" +#include <sys/fcntl.h> +#include <sys/inotify.h> +#include <sys/specialfd.h> +#include "un-namespace.h" +#include "libc_private.h" + +/* + * Provide compatibility with libinotify, which uses different values for these + * flags. + */ +#define IN_NONBLOCK_OLD 0x80000 +#define IN_CLOEXEC_OLD 0x00800 + +int +inotify_add_watch(int fd, const char *pathname, uint32_t mask) +{ + return (inotify_add_watch_at(fd, AT_FDCWD, pathname, mask)); +} + +int +inotify_init1(int flags) +{ + struct specialfd_inotify args; + + if ((flags & IN_NONBLOCK_OLD) != 0) { + flags &= ~IN_NONBLOCK_OLD; + flags |= IN_NONBLOCK; + } + if ((flags & IN_CLOEXEC_OLD) != 0) { + flags &= ~IN_CLOEXEC_OLD; + flags |= IN_CLOEXEC; + } + args.flags = flags; + return (__sys___specialfd(SPECIALFD_INOTIFY, &args, sizeof(args))); +} + +int +inotify_init(void) +{ + return (inotify_init1(0)); +} diff --git a/lib/libc/gen/jrand48.c b/lib/libc/gen/jrand48.c index 0a9f780a9e5c..93442439d49e 100644 --- a/lib/libc/gen/jrand48.c +++ b/lib/libc/gen/jrand48.c @@ -11,14 +11,13 @@ * to anyone/anything when using this software. */ -#include <stdint.h> - #include "rand48.h" long jrand48(unsigned short xseed[3]) { + uint48 tmp; - _dorand48(xseed); - return ((int32_t)(((uint32_t)xseed[2] << 16) | (uint32_t)xseed[1])); + DORAND48(tmp, xseed); + return ((int)((tmp >> 16) & 0xffffffff)); } diff --git a/lib/libc/gen/lcong48.c b/lib/libc/gen/lcong48.c index f13826b3d3f3..871b2110ed94 100644 --- a/lib/libc/gen/lcong48.c +++ b/lib/libc/gen/lcong48.c @@ -13,18 +13,10 @@ #include "rand48.h" -extern unsigned short _rand48_seed[3]; -extern unsigned short _rand48_mult[3]; -extern unsigned short _rand48_add; - void lcong48(unsigned short p[7]) { - _rand48_seed[0] = p[0]; - _rand48_seed[1] = p[1]; - _rand48_seed[2] = p[2]; - _rand48_mult[0] = p[3]; - _rand48_mult[1] = p[4]; - _rand48_mult[2] = p[5]; + LOADRAND48(_rand48_seed, &p[0]); + LOADRAND48(_rand48_mult, &p[3]); _rand48_add = p[6]; } diff --git a/lib/libc/gen/libc_interposing_table.c b/lib/libc/gen/libc_interposing_table.c index d15d32711910..025a67ac3eac 100644 --- a/lib/libc/gen/libc_interposing_table.c +++ b/lib/libc/gen/libc_interposing_table.c @@ -42,7 +42,7 @@ interpos_func_t __libc_interposing[INTERPOS_MAX] = { SLOT(spinlock, __libc_spinlock_stub), SLOT(spinunlock, __libc_spinunlock_stub), SLOT(map_stacks_exec, __libc_map_stacks_exec), - SLOT(distribute_static_tls, __libc_distribute_static_tls), + SLOT(uexterr_gettext, __libc_uexterr_gettext), }; #undef SLOT diff --git a/lib/libc/gen/lrand48.c b/lib/libc/gen/lrand48.c index a3d0111cf4d5..cc07044b8af9 100644 --- a/lib/libc/gen/lrand48.c +++ b/lib/libc/gen/lrand48.c @@ -13,11 +13,9 @@ #include "rand48.h" -extern unsigned short _rand48_seed[3]; - long lrand48(void) { - _dorand48(_rand48_seed); - return ((long) _rand48_seed[2] << 15) + ((long) _rand48_seed[1] >> 1); + _DORAND48(_rand48_seed); + return (_rand48_seed >> 17) & 0x7fffffff; } diff --git a/lib/libc/gen/mrand48.c b/lib/libc/gen/mrand48.c index 15b0bfb1bd6e..f9128a6d4188 100644 --- a/lib/libc/gen/mrand48.c +++ b/lib/libc/gen/mrand48.c @@ -15,13 +15,9 @@ #include "rand48.h" -extern unsigned short _rand48_seed[3]; - long mrand48(void) { - - _dorand48(_rand48_seed); - return ((int32_t)(((uint32_t)_rand48_seed[2] << 16) | - (uint32_t)_rand48_seed[1])); + _DORAND48(_rand48_seed); + return ((int)((_rand48_seed >> 16) & 0xffffffff)); } diff --git a/lib/libc/gen/nrand48.c b/lib/libc/gen/nrand48.c index 6c54065e7e0f..f6f4e231105c 100644 --- a/lib/libc/gen/nrand48.c +++ b/lib/libc/gen/nrand48.c @@ -16,6 +16,8 @@ long nrand48(unsigned short xseed[3]) { - _dorand48(xseed); - return ((long) xseed[2] << 15) + ((long) xseed[1] >> 1); + uint48 tmp; + + DORAND48(tmp, xseed); + return ((tmp >> 17) & 0x7fffffff); } diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c index 956c92c321e8..08d9eb10eaa2 100644 --- a/lib/libc/gen/opendir.c +++ b/lib/libc/gen/opendir.c @@ -30,14 +30,7 @@ */ #include "namespace.h" -#include <sys/param.h> - #include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #include "un-namespace.h" #include "gen-private.h" diff --git a/lib/libc/gen/opendir2.c b/lib/libc/gen/opendir2.c index 30a9030693e4..c5c2e662efd8 100644 --- a/lib/libc/gen/opendir2.c +++ b/lib/libc/gen/opendir2.c @@ -30,11 +30,12 @@ */ #include "namespace.h" -#include <sys/param.h> +#include <sys/types.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <stdbool.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -52,8 +53,7 @@ __opendir2(const char *name, int flags) if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) return (NULL); - if ((fd = _open(name, - O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) + if ((fd = _open(name, O_DIRECTORY | O_RDONLY | O_CLOEXEC)) == -1) return (NULL); dir = __opendir_common(fd, flags, false); @@ -243,20 +243,18 @@ _filldir(DIR *dirp, bool use_current_pos) return (true); } +/* + * Return true if the file descriptor is associated with a file from a + * union file system or from a file system mounted with the union flag. + */ static bool is_unionstack(int fd) { - int unionstack; - - unionstack = _fcntl(fd, F_ISUNIONSTACK, 0); - if (unionstack != -1) - return (unionstack); - /* - * Should not happen unless running on a kernel without the op, - * but no use rendering the system useless in such a case. + * This call shouldn't fail, but if it does, just assume that the + * answer is no. */ - return (0); + return (_fcntl(fd, F_ISUNIONSTACK, 0) > 0); } /* @@ -266,6 +264,7 @@ DIR * __opendir_common(int fd, int flags, bool use_current_pos) { DIR *dirp; + ssize_t ret; int incr; int saved_errno; bool unionstack; @@ -315,13 +314,11 @@ __opendir_common(int fd, int flags, bool use_current_pos) * to prime dd_seek. This also checks if the * fd passed to fdopendir() is a directory. */ - dirp->dd_size = _getdirentries(dirp->dd_fd, + ret = _getdirentries(dirp->dd_fd, dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); - if (dirp->dd_size < 0) { - if (errno == EINVAL) - errno = ENOTDIR; + if (ret < 0) goto fail; - } + dirp->dd_size = (size_t)ret; dirp->dd_flags |= __DTF_SKIPREAD; } else { dirp->dd_size = 0; diff --git a/lib/libc/gen/posix_spawn.c b/lib/libc/gen/posix_spawn.c index ee2ce05ff317..a5b732696b8c 100644 --- a/lib/libc/gen/posix_spawn.c +++ b/lib/libc/gen/posix_spawn.c @@ -261,7 +261,7 @@ _posix_spawn_thr(void *data) } envp = psa->envp != NULL ? psa->envp : environ; if (psa->use_env_path) - execvpe(psa->path, psa->argv, envp); + __libc_execvpe(psa->path, psa->argv, envp); else _execve(psa->path, psa->argv, envp); psa->error = errno; diff --git a/lib/libc/gen/psignal.3 b/lib/libc/gen/psignal.3 index cd3cef0b44a9..bf6a99b4b113 100644 --- a/lib/libc/gen/psignal.3 +++ b/lib/libc/gen/psignal.3 @@ -25,14 +25,17 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 30, 2016 +.Dd September 23, 2025 .Dt PSIGNAL 3 .Os .Sh NAME .Nm psignal , +.Nm psiginfo , .Nm strsignal , .Nm sys_siglist , -.Nm sys_signame +.Nm sys_signame , +.Nm sig2str , +.Nm str2sig .Nd system signal messages .Sh LIBRARY .Lb libc @@ -40,11 +43,17 @@ .In signal.h .Ft void .Fn psignal "int sig" "const char *s" +.Ft void +.Fn psiginfo "const siginfo_t *si" "const char *s" .Vt extern const char * const sys_siglist[] ; .Vt extern const char * const sys_signame[] ; .In string.h .Ft "char *" .Fn strsignal "int sig" +.Ft int +.Fn sig2str "int signum" "char *str" +.Ft int +.Fn str2sig "char *str" "int *pnum" .Sh DESCRIPTION The .Fn psignal @@ -79,6 +88,16 @@ the string .Dq "Unknown signal" is produced. .Pp +The +.Fn psiginfo +function is similar to +.Fn psignal , +except that the signal number information is taken from the +.Fa si +argument which is a +.Vt siginfo_t +structure. +.Pp The message strings can be accessed directly through the external array .Va sys_siglist , @@ -95,12 +114,72 @@ contains a count of the strings in .Va sys_siglist and .Va sys_signame . +.Pp +The +.Fn sig2str +function translates the signal number +.Fa signum +to the signal name, without the +.Dq SIG +prefix, and stores it at the location specified by +.Fa str , +which should be large enough to hold the name and the terminating +.Dv NUL +byte. +The symbol +.Dv SIG2STR_MAX +gives the maximum size in bytes required. +.Pp +The +.Fn str2sig +function translates the signal name +.Fa str +to a signal number and stores it in the location referenced by +.Fa pnum . +The name in +.Fa str +can be either the name of the signal, with or without the +.Dq SIG +prefix, or a decimal number. +.Sh RETURN VALUES +The +.Fn sig2str +and +.Fn str2sig +return 0 on success and -1 on translation failure. +In the latter case the memory to store the translation result is left intact. .Sh SEE ALSO .Xr sigaction 2 , .Xr perror 3 , .Xr strerror 3 +.Sh STANDARDS +The +.Fn psignal +and +.Fn psiginfo +functions are defined by +.St -p1003.1-2008 +, while the +.Fn sig2str +and +.Fn str2sig +functions are defined by +.St -p1003.1-2024 . .Sh HISTORY The .Fn psignal function appeared in .Bx 4.2 . +The +.Fn psiginfo +function appeared in +.Fx 14.3 , +.Nx 6.0 , +and +.Dx 4.1 . +The +.Fn sig2str +and +.Fn str2sig +functions appeared in +.Fx 15.0 . diff --git a/lib/libc/gen/psignal.c b/lib/libc/gen/psignal.c index ab044f683d86..291a6a9337a0 100644 --- a/lib/libc/gen/psignal.c +++ b/lib/libc/gen/psignal.c @@ -55,3 +55,9 @@ psignal(int sig, const char *s) (void)_write(STDERR_FILENO, c, strlen(c)); (void)_write(STDERR_FILENO, "\n", 1); } + +void +psiginfo(const siginfo_t *si, const char *s) +{ + psignal(si->si_signo, s); +} diff --git a/lib/libc/gen/rand48.3 b/lib/libc/gen/rand48.3 index 1e47c843058e..3ea649354270 100644 --- a/lib/libc/gen/rand48.3 +++ b/lib/libc/gen/rand48.3 @@ -9,7 +9,7 @@ .\" of any kind. I shall in no event be liable for anything that happens .\" to anyone/anything when using this software. .\" -.Dd September 4, 2012 +.Dd September 11, 2025 .Dt RAND48 3 .Os .Sh NAME @@ -183,5 +183,8 @@ generator calls. .Xr arc4random 3 , .Xr rand 3 , .Xr random 3 +.Sh STANDARDS +The functions described in this page are expected to conform to +.St -p1003.1-2008 . .Sh AUTHORS .An Martin Birgmeier diff --git a/lib/libc/gen/rand48.h b/lib/libc/gen/rand48.h index 9861e99683cb..d3326e851491 100644 --- a/lib/libc/gen/rand48.h +++ b/lib/libc/gen/rand48.h @@ -14,10 +14,11 @@ #ifndef _RAND48_H_ #define _RAND48_H_ +#include <sys/types.h> #include <math.h> #include <stdlib.h> -void _dorand48(unsigned short[3]); +#include "fpmath.h" #define RAND48_SEED_0 (0x330e) #define RAND48_SEED_1 (0xabcd) @@ -27,4 +28,62 @@ void _dorand48(unsigned short[3]); #define RAND48_MULT_2 (0x0005) #define RAND48_ADD (0x000b) +typedef uint64_t uint48; + +extern uint48 _rand48_seed; +extern uint48 _rand48_mult; +extern uint48 _rand48_add; + +#define TOUINT48(x, y, z) \ + ((uint48)(x) + (((uint48)(y)) << 16) + (((uint48)(z)) << 32)) + +#define RAND48_SEED TOUINT48(RAND48_SEED_0, RAND48_SEED_1, RAND48_SEED_2) +#define RAND48_MULT TOUINT48(RAND48_MULT_0, RAND48_MULT_1, RAND48_MULT_2) + +#define LOADRAND48(l, x) do { \ + (l) = TOUINT48((x)[0], (x)[1], (x)[2]); \ +} while (0) + +#define STORERAND48(l, x) do { \ + (x)[0] = (unsigned short)(l); \ + (x)[1] = (unsigned short)((l) >> 16); \ + (x)[2] = (unsigned short)((l) >> 32); \ +} while (0) + +#define _DORAND48(l) do { \ + (l) = (l) * _rand48_mult + _rand48_add; \ +} while (0) + +#define DORAND48(l, x) do { \ + LOADRAND48(l, x); \ + _DORAND48(l); \ + STORERAND48(l, x); \ +} while (0) + +#define ERAND48_BEGIN \ + union { \ + union IEEEd2bits ieee; \ + uint64_t u64; \ + } u; \ + int s + +/* + * Optimization for speed: assume doubles are IEEE 754 and use bit fiddling + * rather than converting to double. Specifically, clamp the result to 48 bits + * and convert to a double in [0.0, 1.0) via division by 2^48. Normalize by + * shifting the most significant bit into the implicit one position and + * adjusting the exponent accordingly. The store to the exponent field + * overwrites the implicit one. + */ +#define ERAND48_END(x) do { \ + u.u64 = ((x) & 0xffffffffffffULL); \ + if (u.u64 == 0) \ + return (0.0); \ + u.u64 <<= 5; \ + for (s = 0; !(u.u64 & (1LL << 52)); s++, u.u64 <<= 1) \ + ; \ + u.ieee.bits.exp = 1022 - s; \ + return (u.ieee.d); \ +} while (0) + #endif /* _RAND48_H_ */ diff --git a/lib/libc/gen/readdir.c b/lib/libc/gen/readdir.c index 2a2fa999b7ce..94d2b2e8d877 100644 --- a/lib/libc/gen/readdir.c +++ b/lib/libc/gen/readdir.c @@ -48,8 +48,9 @@ struct dirent * _readdir_unlocked(DIR *dirp, int flags) { struct dirent *dp; - long initial_seek; - long initial_loc = 0; + off_t initial_seek; + size_t initial_loc = 0; + ssize_t ret; for (;;) { if (dirp->dd_loc >= dirp->dd_size) { @@ -61,11 +62,13 @@ _readdir_unlocked(DIR *dirp, int flags) } if (dirp->dd_loc == 0 && !(dirp->dd_flags & (__DTF_READALL | __DTF_SKIPREAD))) { + dirp->dd_size = 0; initial_seek = dirp->dd_seek; - dirp->dd_size = _getdirentries(dirp->dd_fd, + ret = _getdirentries(dirp->dd_fd, dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); - if (dirp->dd_size <= 0) + if (ret <= 0) return (NULL); + dirp->dd_size = (size_t)ret; _fixtelldir(dirp, initial_seek, initial_loc); } dirp->dd_flags &= ~__DTF_SKIPREAD; @@ -131,3 +134,5 @@ __readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) } __strong_reference(__readdir_r, readdir_r); +__warn_references(readdir_r, + "warning: this program uses readdir_r(), which is unsafe."); diff --git a/lib/libc/gen/rtld_get_var.3 b/lib/libc/gen/rtld_get_var.3 index e211d78345e3..092114e86d78 100644 --- a/lib/libc/gen/rtld_get_var.3 +++ b/lib/libc/gen/rtld_get_var.3 @@ -103,4 +103,4 @@ is unknown. The .Nm function first appeared in -.Fx 15.0 . +.Fx 14.3 . diff --git a/lib/libc/gen/scandir.3 b/lib/libc/gen/scandir.3 index 6656842c251f..3da4500cefb9 100644 --- a/lib/libc/gen/scandir.3 +++ b/lib/libc/gen/scandir.3 @@ -25,13 +25,16 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd August 31, 2023 +.Dd June 25, 2025 .Dt SCANDIR 3 .Os .Sh NAME .Nm scandir , +.Nm fdscandir , .Nm scandirat , .Nm scandir_b , +.Nm fdscandir_b , +.Nm fdscandirat_b , .Nm alphasort , .Nm versionsort .Nd scan a directory @@ -47,6 +50,13 @@ .Fa "int \*(lp*compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp" .Fc .Ft int +.Fo fdscandir +.Fa "int dirfd" +.Fa "struct dirent ***namelist" +.Fa "int \*(lp*select\*(rp\*(lpconst struct dirent *\*(rp" +.Fa "int \*(lp*compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp" +.Fc +.Ft int .Fo scandirat .Fa "int dirfd" .Fa "const char *dirname" @@ -62,6 +72,21 @@ .Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp" .Fc .Ft int +.Fo fdscandir_b +.Fa "int dirfd" +.Fa "struct dirent ***namelist" +.Fa "int \*(lp^select\*(rp\*(lpconst struct dirent *\*(rp" +.Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp" +.Fc +.Ft int +.Fo scandirat_b +.Fa "int dirfd" +.Fa "const char *dirname" +.Fa "struct dirent ***namelist" +.Fa "int \*(lp^select\*(rp\*(lpconst struct dirent *\*(rp" +.Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp" +.Fc +.Ft int .Fn alphasort "const struct dirent **d1" "const struct dirent **d2" .Ft int .Fn versionsort "const struct dirent **d1" "const struct dirent **d2" @@ -77,7 +102,8 @@ entries using It returns the number of entries in the array. A pointer to the array of directory entries is stored in the location referenced by -.Fa namelist . +.Fa namelist +(even if no entries were selected). .Pp The .Fa select @@ -118,6 +144,13 @@ The memory allocated for the array can be deallocated with by freeing each pointer in the array and then the array itself. .Pp The +.Fn fdscandir +function is similar to +.Fn scandir , +but takes a file descriptor referencing a directory instead of a path. +The file descriptor is left open on return, regardless of outcome. +.Pp +The .Fn scandirat function is similar to .Fn scandir , @@ -151,17 +184,67 @@ See for additional details. .Pp The -.Fn scandir_b -function behaves in the same way as +.Fn scandir_b , +.Fn fdscandir_b , +and +.Fn scandirat_b +functions behave in the same way as .Fn scandir , -but takes blocks as arguments instead of function pointers and calls +.Fn fdscandir , +and +.Fn scandirat , +respectively, +but take blocks as arguments instead of function pointers and call .Fn qsort_b rather than .Fn qsort . .Sh DIAGNOSTICS -Returns \-1 if the directory cannot be opened for reading or if +The +.Fn scandir , +.Fn fdscandir , +.Fn scandirat , +.Fn scandir_b , +.Fn fdscandir_b , +and +.Fn scandirat_b +functions return the number of directory entries found on succes. +If the directory cannot be opened for reading, an error occurs +while reading the directory, or .Xr malloc 3 -cannot allocate enough memory to hold all the data structures. +cannot allocate enough memory to hold all the directory entries, +they return \-1 and set +.Va errno +to an appropriate value. +.Sh ERRORS +The +.Fn scandir , +.Fn scandirat , +.Fn scandir_b , +and +.Fn scandirat_b +functions may fail and set +.Va errno +for any of the errors specified for the +.Xr opendir 3 , +.Xr malloc 3 , +.Xr readdir 3 , +and +.Xr closedir 3 +functions. +.Pp +The +.Fn fdscandir +and +.Fn fdscandir_b +functions may fail and set +.Va errno +for any of the errors specified for the +.Xr fdopendir 3 , +.Xr malloc 3 , +.Xr readdir 3 , +and +.Xr closedir 3 +functions. .Sh SEE ALSO .Xr openat 2 , .Xr directory 3 , @@ -172,8 +255,25 @@ cannot allocate enough memory to hold all the data structures. .Xr dir 5 .Sh STANDARDS The +.Fn alphasort +and +.Fn scandir +functions are expected to conform to +.St -p1003.1-2008 . +The +.Fn scandirat +and .Fn versionsort -function is a GNU extension and conforms to no standard. +functions are GNU extensions and conform to no standard. +The +.Fn fdscandir , +.Fn scandir_b , +.Fn fdscandir_b , +and +.Fn scandirat_b +functions are +.Fx +extensions. .Sh HISTORY The .Fn scandir @@ -182,8 +282,19 @@ and functions appeared in .Bx 4.2 . The +.Fn scandir_b +function was added in +.Fx 11.0 . +The .Fn scandirat and .Fn versionsort functions were added in .Fx 13.2 . +The +.Fn fdscandir , +.Fn fdscandir_b , +and +.Fn scandirat_b +functions were added in +.Fx 15.0 . diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c index f59f57047278..fb589f4b36b6 100644 --- a/lib/libc/gen/scandir.c +++ b/lib/libc/gen/scandir.c @@ -38,6 +38,7 @@ #include "namespace.h" #include <dirent.h> +#include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> @@ -64,71 +65,76 @@ static int scandir_thunk_cmp(const void *p1, const void *p2, void *thunk); static int #ifdef I_AM_SCANDIR_B -scandir_b_dirp(DIR *dirp, struct dirent ***namelist, select_block select, +scandir_dirp_b(DIR *dirp, struct dirent ***namelist, select_block select, dcomp_block dcomp) #else scandir_dirp(DIR *dirp, struct dirent ***namelist, - int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, - const struct dirent **)) + int (*select)(const struct dirent *), + int (*dcomp)(const struct dirent **, const struct dirent **)) #endif { - struct dirent *d, *p, **names = NULL; - size_t arraysz, numitems; + struct dirent *d, *p = NULL, **names = NULL, **names2; + size_t arraysz = 32, numitems = 0; + int serrno; - numitems = 0; - arraysz = 32; /* initial estimate of the array size */ - names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *)); + names = malloc(arraysz * sizeof(*names)); if (names == NULL) - goto fail; + return (-1); - while ((d = readdir(dirp)) != NULL) { + while (errno = 0, (d = readdir(dirp)) != NULL) { if (select != NULL && !SELECT(d)) continue; /* just selected names */ /* * Make a minimum size copy of the data */ - p = (struct dirent *)malloc(_GENERIC_DIRSIZ(d)); + p = malloc(_GENERIC_DIRSIZ(d)); if (p == NULL) goto fail; p->d_fileno = d->d_fileno; p->d_type = d->d_type; p->d_reclen = d->d_reclen; p->d_namlen = d->d_namlen; - bcopy(d->d_name, p->d_name, p->d_namlen + 1); + memcpy(p->d_name, d->d_name, p->d_namlen + 1); /* * Check to make sure the array has space left and * realloc the maximum size. */ if (numitems >= arraysz) { - struct dirent **names2; - - names2 = reallocarray(names, arraysz, - 2 * sizeof(struct dirent *)); - if (names2 == NULL) { - free(p); + arraysz = arraysz * 2; + names2 = reallocarray(names, arraysz, sizeof(*names)); + if (names2 == NULL) goto fail; - } names = names2; - arraysz *= 2; } names[numitems++] = p; } - closedir(dirp); - if (numitems && dcomp != NULL) + /* + * Since we can't simultaneously return both -1 and a count, we + * must either suppress the error or discard the partial result. + * The latter seems the lesser of two evils. + */ + if (errno != 0) + goto fail; + if (numitems > 0 && dcomp != NULL) { #ifdef I_AM_SCANDIR_B - qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp); + qsort_b(names, numitems, sizeof(struct dirent *), + (void *)dcomp); #else qsort_r(names, numitems, sizeof(struct dirent *), scandir_thunk_cmp, &dcomp); #endif + } *namelist = names; return (numitems); fail: + serrno = errno; + if (numitems == 0 || names[numitems - 1] != p) + free(p); while (numitems > 0) free(names[--numitems]); free(names); - closedir(dirp); + errno = serrno; return (-1); } @@ -138,44 +144,87 @@ scandir_b(const char *dirname, struct dirent ***namelist, select_block select, dcomp_block dcomp) #else scandir(const char *dirname, struct dirent ***namelist, - int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, - const struct dirent **)) + int (*select)(const struct dirent *), + int (*dcomp)(const struct dirent **, const struct dirent **)) #endif { DIR *dirp; + int ret, serrno; dirp = opendir(dirname); if (dirp == NULL) return (-1); - return ( + ret = #ifdef I_AM_SCANDIR_B - scandir_b_dirp + scandir_dirp_b #else scandir_dirp #endif - (dirp, namelist, select, dcomp)); + (dirp, namelist, select, dcomp); + serrno = errno; + closedir(dirp); + errno = serrno; + return (ret); } -#ifndef I_AM_SCANDIR_B int -scandirat(int dirfd, const char *dirname, struct dirent ***namelist, - int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, - const struct dirent **)) +#ifdef I_AM_SCANDIR_B +fdscandir_b(int dirfd, struct dirent ***namelist, select_block select, + dcomp_block dcomp) +#else +fdscandir(int dirfd, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*dcomp)(const struct dirent **, const struct dirent **)) +#endif { DIR *dirp; - int fd; + int ret, serrno; + + dirp = fdopendir(dirfd); + if (dirp == NULL) + return (-1); + ret = +#ifdef I_AM_SCANDIR_B + scandir_dirp_b +#else + scandir_dirp +#endif + (dirp, namelist, select, dcomp); + serrno = errno; + fdclosedir(dirp); + errno = serrno; + return (ret); +} + +int +#ifdef I_AM_SCANDIR_B +scandirat_b(int dirfd, const char *dirname, struct dirent ***namelist, + select_block select, dcomp_block dcomp) +#else +scandirat(int dirfd, const char *dirname, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*dcomp)(const struct dirent **, const struct dirent **)) +#endif +{ + int fd, ret, serrno; fd = _openat(dirfd, dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC); if (fd == -1) return (-1); - dirp = fdopendir(fd); - if (dirp == NULL) { - _close(fd); - return (-1); - } - return (scandir_dirp(dirp, namelist, select, dcomp)); + ret = +#ifdef I_AM_SCANDIR_B + fdscandir_b +#else + fdscandir +#endif + (fd, namelist, select, dcomp); + serrno = errno; + _close(fd); + errno = serrno; + return (ret); } +#ifndef I_AM_SCANDIR_B /* * Alphabetic order comparison routine for those who want it. * POSIX 2008 requires that alphasort() uses strcoll(). diff --git a/lib/libc/gen/seed48.c b/lib/libc/gen/seed48.c index 258c4bac3c9f..f57656ce1121 100644 --- a/lib/libc/gen/seed48.c +++ b/lib/libc/gen/seed48.c @@ -13,24 +13,14 @@ #include "rand48.h" -extern unsigned short _rand48_seed[3]; -extern unsigned short _rand48_mult[3]; -extern unsigned short _rand48_add; - unsigned short * seed48(unsigned short xseed[3]) { static unsigned short sseed[3]; - sseed[0] = _rand48_seed[0]; - sseed[1] = _rand48_seed[1]; - sseed[2] = _rand48_seed[2]; - _rand48_seed[0] = xseed[0]; - _rand48_seed[1] = xseed[1]; - _rand48_seed[2] = xseed[2]; - _rand48_mult[0] = RAND48_MULT_0; - _rand48_mult[1] = RAND48_MULT_1; - _rand48_mult[2] = RAND48_MULT_2; + STORERAND48(_rand48_seed, sseed); + LOADRAND48(_rand48_seed, xseed); + _rand48_mult = RAND48_MULT; _rand48_add = RAND48_ADD; - return sseed; + return (sseed); } diff --git a/lib/libc/gen/sig2str.c b/lib/libc/gen/sig2str.c new file mode 100644 index 000000000000..869a09ab9db4 --- /dev/null +++ b/lib/libc/gen/sig2str.c @@ -0,0 +1,113 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Ricardo Branco <rbranco@suse.de>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Translate between signal names and numbers + */ +#include "namespace.h" +#include <ctype.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ssp/ssp.h> +#include "un-namespace.h" + +static const char rtmin_str[] = "RTMIN"; +static const char rtmax_str[] = "RTMAX"; + +int +__ssp_real(sig2str)(int signum, char *str) +{ + if (signum <= 0 || signum > SIGRTMAX) + return (-1); + + if (signum < sys_nsig) + (void)strlcpy(str, sys_signame[signum], SIG2STR_MAX); + else if (signum < SIGRTMIN) + (void)snprintf(str, SIG2STR_MAX, "%d", signum); + else if (signum == SIGRTMIN) + (void)strlcpy(str, rtmin_str, SIG2STR_MAX); + else if (signum == SIGRTMAX) + (void)strlcpy(str, rtmax_str, SIG2STR_MAX); + else if (signum <= (SIGRTMIN + SIGRTMAX) / 2) + (void)snprintf(str, SIG2STR_MAX, "%s+%d", + rtmin_str, signum - SIGRTMIN); + else + (void)snprintf(str, SIG2STR_MAX, "%s-%d", + rtmax_str, SIGRTMAX - signum); + + return (0); +} + +int +str2sig(const char * restrict str, int * restrict pnum) +{ + const char *errstr; + long long n; + int sig; + int rtend = sizeof(rtmin_str) - 1; + + if (strncasecmp(str, "SIG", 3) == 0) + str += 3; + + if (strncasecmp(str, rtmin_str, sizeof(rtmin_str) - 1) == 0 || + strncasecmp(str, rtmax_str, sizeof(rtmax_str) - 1) == 0) { + sig = (toupper(str[4]) == 'X') ? SIGRTMAX : SIGRTMIN; + n = 0; + if (str[rtend] == '+' || str[rtend] == '-') { + n = strtonum(str + rtend, INT_MIN, INT_MAX, &errstr); + if (n == 0 || errstr != NULL) + return (-1); + } else if (str[rtend] != '\0') { + return (-1); + } + sig += (int)n; + if (sig < SIGRTMIN || sig > SIGRTMAX) + return (-1); + *pnum = sig; + return (0); + } + + if (isdigit((unsigned char)str[0])) { + n = strtonum(str, 1, SIGRTMAX, &errstr); + if (errstr == NULL) { + *pnum = (int)n; + return (0); + } + } + + for (sig = 1; sig < sys_nsig; sig++) { + if (strcasecmp(sys_signame[sig], str) == 0) { + *pnum = sig; + return (0); + } + } + + return (-1); +} diff --git a/lib/libc/gen/srand48.c b/lib/libc/gen/srand48.c index fd369a094c51..4b82ece72db8 100644 --- a/lib/libc/gen/srand48.c +++ b/lib/libc/gen/srand48.c @@ -13,18 +13,11 @@ #include "rand48.h" -extern unsigned short _rand48_seed[3]; -extern unsigned short _rand48_mult[3]; -extern unsigned short _rand48_add; - void srand48(long seed) { - _rand48_seed[0] = RAND48_SEED_0; - _rand48_seed[1] = (unsigned short) seed; - _rand48_seed[2] = (unsigned short) (seed >> 16); - _rand48_mult[0] = RAND48_MULT_0; - _rand48_mult[1] = RAND48_MULT_1; - _rand48_mult[2] = RAND48_MULT_2; + _rand48_seed = TOUINT48(RAND48_SEED_0, (unsigned short)seed, + (unsigned short)(seed >> 16)); + _rand48_mult = RAND48_MULT; _rand48_add = RAND48_ADD; } diff --git a/lib/libc/gen/sysconf.c b/lib/libc/gen/sysconf.c index be42dd238b56..b5b732eed05d 100644 --- a/lib/libc/gen/sysconf.c +++ b/lib/libc/gen/sysconf.c @@ -34,6 +34,7 @@ #include "namespace.h" #include <sys/param.h> +#include <sys/exterrvar.h> #include <sys/time.h> #include <sys/sysctl.h> #include <sys/resource.h> @@ -50,7 +51,7 @@ #include "un-namespace.h" #include "../stdlib/atexit.h" -#include "tzdir.h" /* from ../../../contrib/tzcode/stdtime */ +#include "tzdir.h" /* from ../../../contrib/tzcode */ #include "libc_private.h" #define _PATH_ZONEINFO TZDIR /* from tzfile.h */ @@ -286,6 +287,8 @@ do_NAME_MAX: mib[0] = CTL_P1003_1B; mib[1] = CTL_P1003_1B_MQ_OPEN_MAX; goto yesno; + case _SC_NSIG: + return (_SIG_MAXSIG); case _SC_PAGESIZE: return (getpagesize()); case _SC_RTSIG_MAX: @@ -567,7 +570,7 @@ yesno: case _SC_IPV6: #if _POSIX_IPV6 == 0 sverrno = errno; - value = _socket(PF_INET6, SOCK_DGRAM, 0); + value = _socket(PF_INET6, SOCK_CLOEXEC | SOCK_DGRAM, 0); errno = sverrno; if (value >= 0) { _close(value); @@ -602,6 +605,8 @@ yesno: return (-1); return ((long)value); #endif + case _SC_UEXTERR_MAXLEN: + return (UEXTERROR_MAXLEN); default: errno = EINVAL; diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index fb68ddd60edc..ef897c653728 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 16, 2023 +.Dd July 31, 2025 .Dt SYSCTL 3 .Os .Sh NAME @@ -502,7 +502,6 @@ specifies the current process. .It Dv KERN_PROC_ENV Ta "Set of strings" .It Dv KERN_PROC_AUXV Ta "Elf_Auxinfo []" .It Dv KERN_PROC_RLIMIT Ta "Integer" -.It Dv KERN_PROC_RLIMIT_USAGE Ta "rlim_t []" .It Dv KERN_PROC_PS_STRINGS Ta "Integer" .It Dv KERN_PROC_UMASK Ta "Integer/short" .It Dv KERN_PROC_OSREL Ta "Integer" @@ -511,6 +510,7 @@ specifies the current process. .It Dv KERN_PROC_NFDS Ta "Integer" .It Dv KERN_PROC_SIGFASTBLK Ta "Integer" .It Dv KERN_PROC_VM_LAYOUT Ta "struct kinfo_vm_layout" +.It Dv KERN_PROC_RLIMIT_USAGE Ta "rlim_t []" .It Dv KERN_PROC_KQUEUE Ta "struct kinfo_knote []" .El .Pp @@ -527,8 +527,12 @@ The path of the process' text file is returned. The in-kernel call stacks for the threads of the specified process. .It Dv KERN_PROC_VMMAP The description of the map entries for the process. +Also refer to +.Xr kinfo_getvmmap 3 . .It Dv KERN_PROC_FILEDESC The file descriptors for files opened in the specified process. +Also refer to +.Xr kinfo_getfile 3 . .It Dv KERN_PROC_GROUPS Groups associated with the process. .It Dv KERN_PROC_ENV @@ -549,13 +553,6 @@ Additinal OID name element must be supplied, specifiing the resource name as in .Xr getrlimit 2 . The call returns the given resource limit for the process. -.It Dv KERN_PROC_RLIMIT_USAGE -Like -.Dv KERN_PROC_RLIMIT , -but instead of the limit, returns the accounted resource usage. -For resources which do not have a meaningful current value, -.Li \-1 -is returned. .It Dv KERN_PROC_PS_STRINGS Returns the location of the .Vt ps_strings @@ -585,6 +582,13 @@ Returns the address of the location, if active. .It Dv KERN_PROC_VM_LAYOUT Fills a structure describing process virtual address space layout. +.It Dv KERN_PROC_RLIMIT_USAGE +Like +.Dv KERN_PROC_RLIMIT , +but instead of the limit, returns the accounted resource usage. +For resources which do not have a meaningful current value, +.Li \-1 +is returned. .It Dv KERN_PROC_KQUEUE Fills an array of structures describing events registered with the specified kqueue. @@ -949,6 +953,7 @@ A process without appropriate privilege attempts to set a value. .El .Sh SEE ALSO .Xr confstr 3 , +.Xr kinfo_getproc 3 , .Xr kvm 3 , .Xr sysconf 3 , .Xr sysctl 8 diff --git a/lib/libc/gen/telldir.c b/lib/libc/gen/telldir.c index b751fafd975f..1731cc4d7a2c 100644 --- a/lib/libc/gen/telldir.c +++ b/lib/libc/gen/telldir.c @@ -118,7 +118,7 @@ _seekdir(DIR *dirp, long loc) struct dirent *dp; union ddloc_packed ddloc; off_t loc_seek; - long loc_loc; + size_t loc_loc; ddloc.l = loc; @@ -171,7 +171,7 @@ _seekdir(DIR *dirp, long loc) * fetching a new block to fix any such telldir locations. */ void -_fixtelldir(DIR *dirp, long oldseek, long oldloc) +_fixtelldir(DIR *dirp, off_t oldseek, size_t oldloc) { struct ddloc_mem *lp; diff --git a/lib/libc/gen/telldir.h b/lib/libc/gen/telldir.h index 6d113491e819..02fd52af9060 100644 --- a/lib/libc/gen/telldir.h +++ b/lib/libc/gen/telldir.h @@ -46,9 +46,9 @@ */ struct ddloc_mem { LIST_ENTRY(ddloc_mem) loc_lqe; /* entry in list */ - long loc_index; /* key associated with structure */ + size_t loc_index; /* key associated with structure */ off_t loc_seek; /* magic cookie returned by getdirentries */ - long loc_loc; /* offset of entry in buffer */ + size_t loc_loc; /* offset of entry in buffer */ }; #ifdef __LP64__ @@ -102,7 +102,7 @@ bool _filldir(DIR *, bool); struct dirent *_readdir_unlocked(DIR *, int); void _reclaim_telldir(DIR *); void _seekdir(DIR *, long); -void _fixtelldir(DIR *dirp, long oldseek, long oldloc); +void _fixtelldir(DIR *dirp, off_t oldseek, size_t oldloc); DIR *__opendir_common(int, int, bool); #define RDU_SKIP 0x0001 diff --git a/lib/libc/gen/tls.c b/lib/libc/gen/tls.c index 30b4fc583c98..b26b13d45589 100644 --- a/lib/libc/gen/tls.c +++ b/lib/libc/gen/tls.c @@ -80,13 +80,13 @@ static void *libc_tls_init; void * __libc_tls_get_addr(void *vti) { - uintptr_t *dtv; + struct dtv *dtv; tls_index *ti; dtv = _tcb_get()->tcb_dtv; ti = vti; - return ((char *)(dtv[ti->ti_module + 1] + ti->ti_offset) + - TLS_DTV_OFFSET); + return (dtv->dtv_slots[ti->ti_module - 1].dtvs_tls + + (ti->ti_offset + TLS_DTV_OFFSET)); } #ifdef __i386__ @@ -151,7 +151,8 @@ libc_free_aligned(void *ptr) * where TP points (with bias) to TLS and TCB immediately precedes TLS without * any alignment gap[4]. Only TLS should be aligned. The TCB[0] points to DTV * vector and DTV values are biased by constant value (TLS_DTV_OFFSET) from - * real addresses[5]. + * real addresses. However, like RTLD, we don't actually bias the DTV values, + * instead we compensate in __tls_get_addr for ti_offset's bias. * * [1] Ulrich Drepper: ELF Handling for Thread-Local Storage * www.akkadia.org/drepper/tls.pdf @@ -167,8 +168,6 @@ libc_free_aligned(void *ptr) * but we must follow this rule due to suboptimal _tcb_set() * (aka <ARCH>_SET_TP) implementation. This function doesn't expect TP but * TCB as argument. - * - * [5] I'm not able to validate "values are biased" assertions. */ /* @@ -201,11 +200,9 @@ get_tls_block_ptr(void *tcb, size_t tcbsize) void __libc_free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused) { - Elf_Addr *dtv; - Elf_Addr **tls; + struct dtv *dtv; - tls = (Elf_Addr **)tcb; - dtv = tls[0]; + dtv = ((struct tcb *)tcb)->tcb_dtv; __je_bootstrap_free(dtv); libc_free_aligned(get_tls_block_ptr(tcb, tcbsize)); } @@ -233,7 +230,8 @@ __libc_free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused) void * __libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign) { - Elf_Addr *dtv, **tcb; + struct dtv *dtv; + struct tcb *tcb; char *tls_block, *tls; size_t extra_size, maxalign, post_size, pre_size, tls_block_size; @@ -262,7 +260,7 @@ __libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign) abort(); } memset(tls_block, 0, tls_block_size); - tcb = (Elf_Addr **)(tls_block + pre_size + extra_size); + tcb = (struct tcb *)(tls_block + pre_size + extra_size); tls = (char *)tcb + TLS_TCB_SIZE + post_size; if (oldtcb != NULL) { @@ -271,19 +269,20 @@ __libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign) libc_free_aligned(oldtcb); /* Adjust the DTV. */ - dtv = tcb[0]; - dtv[2] = (Elf_Addr)(tls + TLS_DTV_OFFSET); + dtv = tcb->tcb_dtv; + dtv->dtv_slots[0].dtvs_tls = tls; } else { - dtv = __je_bootstrap_malloc(3 * sizeof(Elf_Addr)); + dtv = __je_bootstrap_malloc(sizeof(struct dtv) + + sizeof(struct dtv_slot)); if (dtv == NULL) { tls_msg("__libc_allocate_tls: Out of memory.\n"); abort(); } /* Build the DTV. */ - tcb[0] = dtv; - dtv[0] = 1; /* Generation. */ - dtv[1] = 1; /* Segments count. */ - dtv[2] = (Elf_Addr)(tls + TLS_DTV_OFFSET); + tcb->tcb_dtv = dtv; + dtv->dtv_gen = 1; /* Generation. */ + dtv->dtv_size = 1; /* Segments count. */ + dtv->dtv_slots[0].dtvs_tls = tls; if (libc_tls_init_size > 0) memcpy(tls, libc_tls_init, libc_tls_init_size); @@ -303,8 +302,8 @@ void __libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign) { size_t size; - Elf_Addr* dtv; - Elf_Addr tlsstart, tlsend; + struct dtv *dtv; + uintptr_t tlsstart, tlsend; /* * Figure out the size of the initial TLS block so that we can @@ -313,8 +312,8 @@ __libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign) tcbalign = MAX(tcbalign, libc_tls_init_align); size = roundup2(libc_tls_static_space, tcbalign); - dtv = ((Elf_Addr**)tcb)[1]; - tlsend = (Elf_Addr) tcb; + dtv = ((struct tcb *)tcb)->tcb_dtv; + tlsend = (uintptr_t)tcb; tlsstart = tlsend - size; libc_free_aligned((void*)tlsstart); __je_bootstrap_free(dtv); @@ -324,61 +323,60 @@ __libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign) * Allocate Static TLS using the Variant II method. */ void * -__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) +__libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign) { size_t size; - char *tls; - Elf_Addr *dtv; - Elf_Addr segbase, oldsegbase; + char *tls_block, *tls; + struct dtv *dtv; + struct tcb *tcb; tcbalign = MAX(tcbalign, libc_tls_init_align); size = roundup2(libc_tls_static_space, tcbalign); - if (tcbsize < 2 * sizeof(Elf_Addr)) - tcbsize = 2 * sizeof(Elf_Addr); - tls = libc_malloc_aligned(size + tcbsize, tcbalign); - if (tls == NULL) { + if (tcbsize < 2 * sizeof(uintptr_t)) + tcbsize = 2 * sizeof(uintptr_t); + tls_block = libc_malloc_aligned(size + tcbsize, tcbalign); + if (tls_block == NULL) { tls_msg("__libc_allocate_tls: Out of memory.\n"); abort(); } - memset(tls, 0, size + tcbsize); - dtv = __je_bootstrap_malloc(3 * sizeof(Elf_Addr)); + memset(tls_block, 0, size + tcbsize); + dtv = __je_bootstrap_malloc(sizeof(struct dtv) + + sizeof(struct dtv_slot)); if (dtv == NULL) { tls_msg("__libc_allocate_tls: Out of memory.\n"); abort(); } - segbase = (Elf_Addr)(tls + size); - ((Elf_Addr*)segbase)[0] = segbase; - ((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv; + tcb = (struct tcb *)(tls_block + size); + tls = (char *)tcb - libc_tls_static_space; + tcb->tcb_self = tcb; + tcb->tcb_dtv = dtv; - dtv[0] = 1; - dtv[1] = 1; - dtv[2] = segbase - libc_tls_static_space; + dtv->dtv_gen = 1; + dtv->dtv_size = 1; + dtv->dtv_slots[0].dtvs_tls = tls; - if (oldtls) { + if (oldtcb != NULL) { /* * Copy the static TLS block over whole. */ - oldsegbase = (Elf_Addr) oldtls; - memcpy((void *)(segbase - libc_tls_static_space), - (const void *)(oldsegbase - libc_tls_static_space), + memcpy(tls, (const char *)oldtcb - libc_tls_static_space, libc_tls_static_space); /* * We assume that this block was the one we created with * allocate_initial_tls(). */ - _rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr)); + _rtld_free_tls(oldtcb, 2 * sizeof(uintptr_t), + sizeof(uintptr_t)); } else { - memcpy((void *)(segbase - libc_tls_static_space), - libc_tls_init, libc_tls_init_size); - memset((void *)(segbase - libc_tls_static_space + - libc_tls_init_size), 0, + memcpy(tls, libc_tls_init, libc_tls_init_size); + memset(tls + libc_tls_init_size, 0, libc_tls_static_space - libc_tls_init_size); } - return (void*) segbase; + return (tcb); } #endif /* TLS_VARIANT_II */ @@ -386,7 +384,7 @@ __libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) #else void * -__libc_allocate_tls(void *oldtls __unused, size_t tcbsize __unused, +__libc_allocate_tls(void *oldtcb __unused, size_t tcbsize __unused, size_t tcbalign __unused) { return (0); diff --git a/lib/libc/gen/ualarm.3 b/lib/libc/gen/ualarm.3 index f4e0973b133a..dfd9ddd5f434 100644 --- a/lib/libc/gen/ualarm.3 +++ b/lib/libc/gen/ualarm.3 @@ -70,7 +70,7 @@ restriction the maximum number of .Fa microseconds and .Fa interval -is limited to 100000000000000 +is limited to 100,000,000,000,000 (in case this value fits in the unsigned integer). .Sh RETURN VALUES When the signal has successfully been caught, diff --git a/lib/libc/gen/uexterr_format.c b/lib/libc/gen/uexterr_format.c new file mode 100644 index 000000000000..e8ddfbd578e3 --- /dev/null +++ b/lib/libc/gen/uexterr_format.c @@ -0,0 +1,35 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 The FreeBSD Foundation + * All rights reserved. + * + * This software were developed by Konstantin Belousov <kib@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + */ + +#include <sys/types.h> +#include <sys/exterrvar.h> +#include <exterr.h> +#include <stdio.h> +#include <string.h> + +int +__uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz) +{ + if (bufsz > UEXTERROR_MAXLEN) + bufsz = UEXTERROR_MAXLEN; + if (ue->error == 0) { + strlcpy(buf, "", bufsz); + return (0); + } + if (ue->msg[0] == '\0') { + snprintf(buf, bufsz, + "errno %d category %u (src line %u) p1 %#jx p2 %#jx", + ue->error, ue->cat, ue->src_line, + (uintmax_t)ue->p1, (uintmax_t)ue->p2); + } else { + strlcpy(buf, ue->msg, bufsz); + } + return (0); +} diff --git a/lib/libc/gen/uexterr_gettext.c b/lib/libc/gen/uexterr_gettext.c new file mode 100644 index 000000000000..5b8a5cd17a48 --- /dev/null +++ b/lib/libc/gen/uexterr_gettext.c @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 The FreeBSD Foundation + * All rights reserved. + * + * This software were developed by Konstantin Belousov <kib@FreeBSD.org> + * under sponsorship from the FreeBSD Foundation. + */ + +#define _WANT_P_OSREL +#include <sys/param.h> +#include <sys/exterrvar.h> +#include <exterr.h> +#include <string.h> +#include "libc_private.h" + +static struct uexterror uexterr = { + .ver = UEXTERROR_VER, +}; + +int __getosreldate(void); + +static void uexterr_ctr(void) __attribute__((constructor)); +static void +uexterr_ctr(void) +{ + if (__getosreldate() >= P_OSREL_EXTERRCTL) + exterrctl(EXTERRCTL_ENABLE, 0, &uexterr); +} + +int +__libc_uexterr_gettext(char *buf, size_t bufsz) +{ + return (__uexterr_format(&uexterr, buf, bufsz)); +} + +int +uexterr_gettext(char *buf, size_t bufsz) +{ + return (((int (*)(char *, size_t)) + __libc_interposing[INTERPOS_uexterr_gettext])(buf, bufsz)); +} diff --git a/lib/libc/gen/wordexp.c b/lib/libc/gen/wordexp.c index f1437e30bbe2..f8080c20d121 100644 --- a/lib/libc/gen/wordexp.c +++ b/lib/libc/gen/wordexp.c @@ -265,7 +265,15 @@ cleanup: errno = serrno; return (error); } - if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) + /* + * Check process exit status, but ignore ECHILD as the child may have + * been automatically reaped if the process had set SIG_IGN or + * SA_NOCLDWAIT for SIGCHLD, and our reason for waitpid was just to + * reap our own child on behalf of the calling process. + */ + if (wpid < 0 && errno != ECHILD) + return (WRDE_NOSPACE); /* abort for unknown reason */ + if (wpid >= 0 && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) return (WRDE_NOSPACE); /* abort for unknown reason */ /* diff --git a/lib/libc/i386/gen/makecontext.c b/lib/libc/i386/gen/makecontext.c index 7b4845ac6bee..9fedd8b99c47 100644 --- a/lib/libc/i386/gen/makecontext.c +++ b/lib/libc/i386/gen/makecontext.c @@ -124,7 +124,7 @@ __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) /* * Setup the top of the stack with the user start routine - * followed by all of its aguments and the pointer to the + * followed by all of its arguments and the pointer to the * ucontext. We need to leave a spare spot at the top of * the stack because setcontext will move eip to the top * of the stack before returning. diff --git a/lib/libc/i386/static_tls.h b/lib/libc/i386/static_tls.h deleted file mode 100644 index baf9dfc59a5a..000000000000 --- a/lib/libc/i386/static_tls.h +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by Konstantin Belousov <kib@FreeBSD.org> - * under sponsorship from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _LIBC_I386_STATIC_TLS_H -#define _LIBC_I386_STATIC_TLS_H - -static __inline uintptr_t -_libc_get_static_tls_base(size_t offset) -{ - uintptr_t tlsbase; - - __asm __volatile("movl %%gs:0, %0" : "=r" (tlsbase)); - tlsbase -= offset; - return (tlsbase); -} - -#endif diff --git a/lib/libc/include/block_abi.h b/lib/libc/include/block_abi.h index e04998d6148d..cf670c6c079e 100644 --- a/lib/libc/include/block_abi.h +++ b/lib/libc/include/block_abi.h @@ -37,7 +37,7 @@ * compilers that support blocks and exposing the ABI in the source for other * compilers. */ -#define CALL_BLOCK(name, ...) name(__VA_ARGS__) +#define CALL_BLOCK(name, ...) (name)(__VA_ARGS__) #else // !__BLOCKS__ #define DECLARE_BLOCK(retTy, name, argTys, ...)\ struct {\ diff --git a/lib/libc/include/compat.h b/lib/libc/include/compat.h index 70fb8dcd97f3..630ffe7daae3 100644 --- a/lib/libc/include/compat.h +++ b/lib/libc/include/compat.h @@ -69,6 +69,9 @@ __sym_compat(kevent, freebsd11_kevent, FBSD_1.0); __sym_compat(swapoff, freebsd13_swapoff, FBSD_1.0); +__sym_compat(getgroups, freebsd14_getgroups, FBSD_1.0); +__sym_compat(setgroups, freebsd14_setgroups, FBSD_1.0); + #undef __sym_compat #define __weak_reference(sym,alias) \ @@ -77,4 +80,3 @@ __sym_compat(swapoff, freebsd13_swapoff, FBSD_1.0); #undef __weak_reference #endif /* __LIBC_COMPAT_H__ */ - diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index 4c244f962b2b..db4cbc32be35 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -249,8 +249,9 @@ enum { INTERPOS_map_stacks_exec, INTERPOS_fdatasync, INTERPOS_clock_nanosleep, - INTERPOS_distribute_static_tls, + INTERPOS__reserved0, /* was distribute_static_tls */ INTERPOS_pdfork, + INTERPOS_uexterr_gettext, INTERPOS_MAX }; @@ -345,6 +346,7 @@ struct __ucontext; struct __wrusage; enum idtype; +int __libc_execvpe(const char *, char * const *, char * const *); int __libc_sigaction(int, const struct sigaction *, struct sigaction *) __hidden; int __libc_sigprocmask(int, const __sigset_t *, __sigset_t *) @@ -359,8 +361,6 @@ struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); void __init_elf_aux_vector(void); void __libc_map_stacks_exec(void); -void __libc_distribute_static_tls(__size_t, void *, __size_t, __size_t); -__uintptr_t __libc_static_tls_base(__size_t); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int); @@ -379,4 +379,8 @@ struct __nl_cat_d *__catopen_l(const char *name, int type, int __strerror_rl(int errnum, char *strerrbuf, size_t buflen, struct _xlocale *locale); +struct uexterror; +int __uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz); +int __libc_uexterr_gettext(char *buf, size_t bufsz); + #endif /* _LIBC_PRIVATE_H_ */ diff --git a/lib/libc/include/port_before.h b/lib/libc/include/port_before.h index cfc43c53f157..aa2cd394104a 100644 --- a/lib/libc/include/port_before.h +++ b/lib/libc/include/port_before.h @@ -5,7 +5,6 @@ #define _LIBC 1 #define DO_PTHREADS 1 #define USE_POLL 1 -#define HAVE_MD5 1 #define ISC_SOCKLEN_T socklen_t #define ISC_FORMAT_PRINTF(fmt, args) \ diff --git a/lib/libc/inet/inet_net_ntop.c b/lib/libc/inet/inet_net_ntop.c index 9d98dbb5ca99..30dd5c0571f2 100644 --- a/lib/libc/inet/inet_net_ntop.c +++ b/lib/libc/inet/inet_net_ntop.c @@ -1,20 +1,23 @@ -/*- - * SPDX-License-Identifier: ISC +/* $OpenBSD: inet_net_ntop.c,v 1.9 2019/07/03 03:24:04 deraadt Exp $ */ + +/* + * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org> + * Copyright (c) 1996 by Internet Software Consortium. * - * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1996,1999 by Internet Software Consortium. + * SPDX-License-Identifier: ISC * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #include "port_before.h" @@ -31,18 +34,10 @@ #include "port_after.h" -#ifdef SPRINTF_CHAR -# define SPRINTF(x) strlen(sprintf/**/x) -#else -# define SPRINTF(x) ((size_t)sprintf x) -#endif - -static char * inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, - size_t size); -static char * inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, - size_t size); +static char *inet_net_ntop_ipv4(const u_char *, int, char *, size_t); +static char *inet_net_ntop_ipv6(const u_char *, int, char *, size_t); -/*% +/* * char * * inet_net_ntop(af, src, bits, dst, size) * convert network number from network to presentation format. @@ -66,7 +61,7 @@ inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) } } -/*% +/* * static char * * inet_net_ntop_ipv4(src, bits, dst, size) * convert IPv4 network number from network to presentation format. @@ -83,53 +78,63 @@ static char * inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) { char *odst = dst; - char *t; u_int m; int b; + char *ep; + int advance; + + ep = dst + size; + if (ep <= dst) + goto emsgsize; if (bits < 0 || bits > 32) { errno = EINVAL; return (NULL); } - if (bits == 0) { - if (size < sizeof "0") + if (ep - dst < sizeof "0") goto emsgsize; *dst++ = '0'; - size--; *dst = '\0'; } /* Format whole octets. */ for (b = bits / 8; b > 0; b--) { - if (size <= sizeof "255.") + if (ep - dst < sizeof "255.") + goto emsgsize; + advance = snprintf(dst, ep - dst, "%u", *src++); + if (advance <= 0 || advance >= ep - dst) goto emsgsize; - t = dst; - dst += SPRINTF((dst, "%u", *src++)); + dst += advance; if (b > 1) { + if (dst + 1 >= ep) + goto emsgsize; *dst++ = '.'; *dst = '\0'; } - size -= (size_t)(dst - t); } /* Format partial octet. */ b = bits % 8; if (b > 0) { - if (size <= sizeof ".255") + if (ep - dst < sizeof ".255") goto emsgsize; - t = dst; if (dst != odst) *dst++ = '.'; m = ((1 << b) - 1) << (8 - b); - dst += SPRINTF((dst, "%u", *src & m)); - size -= (size_t)(dst - t); + advance = snprintf(dst, ep - dst, "%u", *src & m); + if (advance <= 0 || advance >= ep - dst) + goto emsgsize; + dst += advance; } /* Format CIDR /width. */ - if (size <= sizeof "/32") + if (ep - dst < sizeof "/32") + goto emsgsize; + advance = snprintf(dst, ep - dst, "/%u", bits); + if (advance <= 0 || advance >= ep - dst) goto emsgsize; - dst += SPRINTF((dst, "/%u", bits)); + dst += advance; return (odst); emsgsize: @@ -137,132 +142,27 @@ inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) return (NULL); } -/*% - * static char * - * inet_net_ntop_ipv6(src, bits, fakebits, dst, size) - * convert IPv6 network number from network to presentation format. - * generates CIDR style result always. Picks the shortest representation - * unless the IP is really IPv4. - * always prints specified number of bits (bits). - * return: - * pointer to dst, or NULL if an error occurred (check errno). - * note: - * network byte order assumed. this means 192.5.5.240/28 has - * 0b11110000 in its fourth octet. - * author: - * Vadim Kogan (UCB), June 2001 - * Original version (IPv4) by Paul Vixie (ISC), July 1996 - */ - static char * -inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) { - u_int m; - int b; - int p; - int zero_s, zero_l, tmp_zero_s, tmp_zero_l; - int i; - int is_ipv4 = 0; - unsigned char inbuf[16]; - char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; - char *cp; - int words; - u_char *s; +inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) +{ + int ret; + char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")]; if (bits < 0 || bits > 128) { errno = EINVAL; return (NULL); } - cp = outbuf; - - if (bits == 0) { - *cp++ = ':'; - *cp++ = ':'; - *cp = '\0'; - } else { - /* Copy src to private buffer. Zero host part. */ - p = (bits + 7) / 8; - memcpy(inbuf, src, p); - memset(inbuf + p, 0, 16 - p); - b = bits % 8; - if (b != 0) { - m = ~0 << (8 - b); - inbuf[p-1] &= m; - } - - s = inbuf; - - /* how many words need to be displayed in output */ - words = (bits + 15) / 16; - if (words == 1) - words = 2; - - /* Find the longest substring of zero's */ - zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0; - for (i = 0; i < (words * 2); i += 2) { - if ((s[i] | s[i+1]) == 0) { - if (tmp_zero_l == 0) - tmp_zero_s = i / 2; - tmp_zero_l++; - } else { - if (tmp_zero_l && zero_l < tmp_zero_l) { - zero_s = tmp_zero_s; - zero_l = tmp_zero_l; - tmp_zero_l = 0; - } - } - } - - if (tmp_zero_l && zero_l < tmp_zero_l) { - zero_s = tmp_zero_s; - zero_l = tmp_zero_l; - } - - if (zero_l != words && zero_s == 0 && ((zero_l == 6) || - ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) || - ((zero_l == 7 && s[14] != 0 && s[15] != 1))))) - is_ipv4 = 1; - - /* Format whole words. */ - for (p = 0; p < words; p++) { - if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) { - /* Time to skip some zeros */ - if (p == zero_s) - *cp++ = ':'; - if (p == words - 1) - *cp++ = ':'; - s++; - s++; - continue; - } + if (inet_ntop(AF_INET6, src, buf, size) == NULL) + return (NULL); - if (is_ipv4 && p > 5 ) { - *cp++ = (p == 6) ? ':' : '.'; - cp += SPRINTF((cp, "%u", *s++)); - /* we can potentially drop the last octet */ - if (p != 7 || bits > 120) { - *cp++ = '.'; - cp += SPRINTF((cp, "%u", *s++)); - } - } else { - if (cp != outbuf) - *cp++ = ':'; - cp += SPRINTF((cp, "%x", *s * 256 + s[1])); - s += 2; - } - } + ret = snprintf(dst, size, "%s/%d", buf, bits); + if (ret < 0 || ret >= size) { + errno = EMSGSIZE; + return (NULL); } - /* Format CIDR /width. */ - sprintf(cp, "/%u", bits); - if (strlen(outbuf) + 1 > size) - goto emsgsize; - strcpy(dst, outbuf); return (dst); - -emsgsize: - errno = EMSGSIZE; - return (NULL); } /* @@ -271,5 +171,3 @@ emsgsize: */ #undef inet_net_ntop __weak_reference(__inet_net_ntop, inet_net_ntop); - -/*! \file */ diff --git a/lib/libc/inet/inet_net_pton.c b/lib/libc/inet/inet_net_pton.c index d566a0e1d3c3..14c88eb72014 100644 --- a/lib/libc/inet/inet_net_pton.c +++ b/lib/libc/inet/inet_net_pton.c @@ -1,20 +1,23 @@ -/*- - * SPDX-License-Identifier: ISC +/* $OpenBSD: inet_net_pton.c,v 1.14 2022/12/27 17:10:06 jmc Exp $ */ + +/* + * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org> + * Copyright (c) 1996,1999 by Internet Software Consortium. * - * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") - * Copyright (C) 1996, 1998, 1999, 2001, 2003 Internet Software Consortium. + * SPDX-License-Identifier: ISC * - * Permission to use, copy, modify, and/or distribute this software for any + * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. */ #include "port_before.h" @@ -22,7 +25,6 @@ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> -#include <arpa/nameser.h> #include <arpa/inet.h> #include <assert.h> @@ -34,13 +36,37 @@ #include "port_after.h" -#ifdef SPRINTF_CHAR -# define SPRINTF(x) strlen(sprintf/**/x) -#else -# define SPRINTF(x) ((size_t)sprintf x) -#endif +static int inet_net_pton_ipv4(const char *, u_char *, size_t); +static int inet_net_pton_ipv6(const char *, u_char *, size_t); -/*% +/* + * static int + * inet_net_pton(af, src, dst, size) + * convert network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not a valid network specification. + * author: + * Paul Vixie (ISC), June 1996 + */ +int +inet_net_pton(int af, const char *src, void *dst, size_t size) +{ + switch (af) { + case AF_INET: + return (inet_net_pton_ipv4(src, dst, size)); + case AF_INET6: + return (inet_net_pton_ipv6(src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (-1); + } +} + +/* * static int * inet_net_pton_ipv4(src, dst, size) * convert IPv4 network number from presentation to network format. @@ -57,22 +83,24 @@ * Paul Vixie (ISC), June 1996 */ static int -inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { - static const char xdigits[] = "0123456789abcdef"; - static const char digits[] = "0123456789"; - int n, ch, tmp = 0, dirty, bits; +inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) +{ + static const char + xdigits[] = "0123456789abcdef", + digits[] = "0123456789"; + int n, ch, tmp, dirty, bits; const u_char *odst = dst; - ch = *src++; + ch = (unsigned char)*src++; if (ch == '0' && (src[0] == 'x' || src[0] == 'X') - && isascii((unsigned char)(src[1])) - && isxdigit((unsigned char)(src[1]))) { + && isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) { /* Hexadecimal: Eat nybble string. */ - if (size <= 0U) + if (size == 0) goto emsgsize; - dirty = 0; - src++; /*%< skip x or X. */ - while ((ch = *src++) != '\0' && isascii(ch) && isxdigit(ch)) { + tmp = 0, dirty = 0; + src++; /* skip x or X. */ + while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isxdigit(ch)) { if (isupper(ch)) ch = tolower(ch); n = strchr(xdigits, ch) - xdigits; @@ -82,14 +110,14 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { else tmp = (tmp << 4) | n; if (++dirty == 2) { - if (size-- <= 0U) + if (size-- == 0) goto emsgsize; *dst++ = (u_char) tmp; dirty = 0; } } - if (dirty) { /*%< Odd trailing nybble? */ - if (size-- <= 0U) + if (dirty) { /* Odd trailing nybble? */ + if (size-- == 0) goto emsgsize; *dst++ = (u_char) (tmp << 4); } @@ -104,16 +132,16 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { tmp += n; if (tmp > 255) goto enoent; - } while ((ch = *src++) != '\0' && + } while ((ch = (unsigned char)*src++) != '\0' && isascii(ch) && isdigit(ch)); - if (size-- <= 0U) + if (size-- == 0) goto emsgsize; *dst++ = (u_char) tmp; if (ch == '\0' || ch == '/') break; if (ch != '.') goto enoent; - ch = *src++; + ch = (unsigned char)*src++; if (!isascii(ch) || !isdigit(ch)) goto enoent; } @@ -121,10 +149,10 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { goto enoent; bits = -1; - if (ch == '/' && isascii((unsigned char)(src[0])) && - isdigit((unsigned char)(src[0])) && dst > odst) { + if (ch == '/' && isascii((unsigned char)src[0]) && + isdigit((unsigned char)src[0]) && dst > odst) { /* CIDR width specifier. Nothing can follow it. */ - ch = *src++; /*%< Skip over the /. */ + ch = (unsigned char)*src++; /* Skip over the /. */ bits = 0; do { n = strchr(digits, ch) - digits; @@ -132,8 +160,9 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { bits *= 10; bits += n; if (bits > 32) - goto enoent; - } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch)); + goto emsgsize; + } while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isdigit(ch)); if (ch != '\0') goto enoent; } @@ -147,29 +176,23 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { goto enoent; /* If no CIDR spec was given, infer width from net class. */ if (bits == -1) { - if (*odst >= 240) /*%< Class E */ + if (*odst >= 240) /* Class E */ bits = 32; - else if (*odst >= 224) /*%< Class D */ - bits = 8; - else if (*odst >= 192) /*%< Class C */ + else if (*odst >= 224) /* Class D */ + bits = 4; + else if (*odst >= 192) /* Class C */ bits = 24; - else if (*odst >= 128) /*%< Class B */ + else if (*odst >= 128) /* Class B */ bits = 16; - else /*%< Class A */ + else /* Class A */ bits = 8; /* If imputed mask is narrower than specified octets, widen. */ if (bits < ((dst - odst) * 8)) bits = (dst - odst) * 8; - /* - * If there are no additional bits specified for a class D - * address adjust bits to 4. - */ - if (bits == 8 && *odst == 224) - bits = 4; } /* Extend network to cover the actual mask. */ while (bits > ((dst - odst) * 8)) { - if (size-- <= 0U) + if (size-- == 0) goto emsgsize; *dst++ = '\0'; } @@ -184,222 +207,48 @@ inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) { return (-1); } -static int -getbits(const char *src, int *bitsp) { - static const char digits[] = "0123456789"; - int n; - int val; - char ch; - - val = 0; - n = 0; - while ((ch = *src++) != '\0') { - const char *pch; - - pch = strchr(digits, ch); - if (pch != NULL) { - if (n++ != 0 && val == 0) /*%< no leading zeros */ - return (0); - val *= 10; - val += (pch - digits); - if (val > 128) /*%< range */ - return (0); - continue; - } - return (0); - } - if (n == 0) - return (0); - *bitsp = val; - return (1); -} static int -getv4(const char *src, u_char *dst, int *bitsp) { - static const char digits[] = "0123456789"; - u_char *odst = dst; - int n; - u_int val; - char ch; - - val = 0; - n = 0; - while ((ch = *src++) != '\0') { - const char *pch; +inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) +{ + struct in6_addr in6; + int ret; + int bits; + size_t bytes; + char buf[INET6_ADDRSTRLEN + sizeof("/128")]; + char *sep; + const char *errstr; - pch = strchr(digits, ch); - if (pch != NULL) { - if (n++ != 0 && val == 0) /*%< no leading zeros */ - return (0); - val *= 10; - val += (pch - digits); - if (val > 255) /*%< range */ - return (0); - continue; - } - if (ch == '.' || ch == '/') { - if (dst - odst > 3) /*%< too many octets? */ - return (0); - *dst++ = val; - if (ch == '/') - return (getbits(src, bitsp)); - val = 0; - n = 0; - continue; - } - return (0); + if (strlcpy(buf, src, sizeof buf) >= sizeof buf) { + errno = EMSGSIZE; + return (-1); } - if (n == 0) - return (0); - if (dst - odst > 3) /*%< too many octets? */ - return (0); - *dst++ = val; - return (1); -} -static int -inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) { - static const char xdigits_l[] = "0123456789abcdef", - xdigits_u[] = "0123456789ABCDEF"; - u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; - const char *xdigits, *curtok; - int ch, saw_xdigit; - u_int val; - int digits; - int bits; - size_t bytes; - int words; - int ipv4; + sep = strchr(buf, '/'); + if (sep != NULL) + *sep++ = '\0'; - memset((tp = tmp), '\0', NS_IN6ADDRSZ); - endp = tp + NS_IN6ADDRSZ; - colonp = NULL; - /* Leading :: requires some special handling. */ - if (*src == ':') - if (*++src != ':') - goto enoent; - curtok = src; - saw_xdigit = 0; - val = 0; - digits = 0; - bits = -1; - ipv4 = 0; - while ((ch = *src++) != '\0') { - const char *pch; + ret = inet_pton(AF_INET6, buf, &in6); + if (ret != 1) + return (-1); - if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) - pch = strchr((xdigits = xdigits_u), ch); - if (pch != NULL) { - val <<= 4; - val |= (pch - xdigits); - if (++digits > 4) - goto enoent; - saw_xdigit = 1; - continue; - } - if (ch == ':') { - curtok = src; - if (!saw_xdigit) { - if (colonp) - goto enoent; - colonp = tp; - continue; - } else if (*src == '\0') - goto enoent; - if (tp + NS_INT16SZ > endp) - return (0); - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - saw_xdigit = 0; - digits = 0; - val = 0; - continue; - } - if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && - getv4(curtok, tp, &bits) > 0) { - tp += NS_INADDRSZ; - saw_xdigit = 0; - ipv4 = 1; - break; /*%< '\\0' was seen by inet_pton4(). */ - } - if (ch == '/' && getbits(src, &bits) > 0) - break; - goto enoent; - } - if (saw_xdigit) { - if (tp + NS_INT16SZ > endp) - goto enoent; - *tp++ = (u_char) (val >> 8) & 0xff; - *tp++ = (u_char) val & 0xff; - } - if (bits == -1) + if (sep == NULL) bits = 128; - - words = (bits + 15) / 16; - if (words < 2) - words = 2; - if (ipv4) - words = 8; - endp = tmp + 2 * words; - - if (colonp != NULL) { - /* - * Since some memmove()'s erroneously fail to handle - * overlapping regions, we'll do the shift by hand. - */ - const int n = tp - colonp; - int i; - - if (tp == endp) - goto enoent; - for (i = 1; i <= n; i++) { - endp[- i] = colonp[n - i]; - colonp[n - i] = 0; + else { + bits = strtonum(sep, 0, 128, &errstr); + if (errstr) { + errno = EINVAL; + return (-1); } - tp = endp; } - if (tp != endp) - goto enoent; bytes = (bits + 7) / 8; - if (bytes > size) - goto emsgsize; - memcpy(dst, tmp, bytes); - return (bits); - - enoent: - errno = ENOENT; - return (-1); - - emsgsize: - errno = EMSGSIZE; - return (-1); -} - -/*% - * int - * inet_net_pton(af, src, dst, size) - * convert network number from presentation to network format. - * accepts hex octets, hex strings, decimal octets, and /CIDR. - * "size" is in bytes and describes "dst". - * return: - * number of bits, either imputed classfully or specified with /CIDR, - * or -1 if some failure occurred (check errno). ENOENT means it was - * not a valid network specification. - * author: - * Paul Vixie (ISC), June 1996 - */ -int -inet_net_pton(int af, const char *src, void *dst, size_t size) { - switch (af) { - case AF_INET: - return (inet_net_pton_ipv4(src, dst, size)); - case AF_INET6: - return (inet_net_pton_ipv6(src, dst, size)); - default: - errno = EAFNOSUPPORT; + if (bytes > size) { + errno = EMSGSIZE; return (-1); } + memcpy(dst, &in6.s6_addr, bytes); + return (bits); } /* @@ -408,5 +257,3 @@ inet_net_pton(int af, const char *src, void *dst, size_t size) { */ #undef inet_net_pton __weak_reference(__inet_net_pton, inet_net_pton); - -/*! \file */ diff --git a/lib/libc/locale/collate.c b/lib/libc/locale/collate.c index 8e3635485f10..c0fc4c91481d 100644 --- a/lib/libc/locale/collate.c +++ b/lib/libc/locale/collate.c @@ -43,6 +43,7 @@ #include <sys/mman.h> #include <assert.h> +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -732,3 +733,261 @@ __collate_equiv_value(locale_t locale, const wchar_t *str, size_t len) } return (0); } + +/* + * __collate_collating_symbol takes the multibyte string specified by + * src and slen, and using ps, converts that to a wide character. Then + * it is checked to verify it is a collating symbol, and then copies + * it to the wide character string specified by dst and dlen (the + * results are not null terminated). The length of the wide characters + * copied to dst is returned if successful. Zero is returned if no such + * collating symbol exists. (size_t)-1 is returned if there are wide-character + * conversion errors, if the length of the converted string is greater that + * COLLATE_STR_LEN or if dlen is too small. It is up to the calling routine to + * preserve the mbstate_t structure as needed. + */ +size_t +__collate_collating_symbol(wchar_t *dst, size_t dlen, const char *src, + size_t slen, mbstate_t *ps) +{ + wchar_t wname[COLLATE_STR_LEN]; + wchar_t w, *wp; + struct xlocale_collate *table; + size_t len, l; + + table = + (struct xlocale_collate *)__get_locale()->components[XLC_COLLATE]; + /* POSIX locale */ + if (table->__collate_load_error) { + if (dlen < 1) + return ((size_t)-1); + if (slen != 1 || !isascii(*src)) + return (0); + *dst = *src; + return (1); + } + for (wp = wname, len = 0; slen > 0; len++) { + l = mbrtowc(&w, src, slen, ps); + if (l == (size_t)-1 || l == (size_t)-2) + return ((size_t)-1); + if (l == 0) + break; + if (len >= COLLATE_STR_LEN) + return ((size_t)-1); + *wp++ = w; + src += l; + slen -= l; + } + if (len == 0 || len > dlen) + return ((size_t)-1); + if (len == 1) { + if (*wname <= UCHAR_MAX) { + if (table->char_pri_table[*wname].pri[0] >= 0) { + if (dlen > 0) + *dst = *wname; + return (1); + } + return (0); + } else if (table->info->large_count > 0) { + collate_large_t *match; + match = largesearch(table, *wname); + if (match && match->pri.pri[0] >= 0) { + if (dlen > 0) + *dst = *wname; + return (1); + } + } + return (0); + } + *wp = 0; + if (table->info->chain_count > 0) { + collate_chain_t *match; + int ll; + match = chainsearch(table, wname, &ll); + if (match) { + if (ll < dlen) + dlen = ll; + wcsncpy(dst, wname, dlen); + return (dlen); + } + } + return (0); +} + +/* + * __collate_equiv_class returns the equivalence class number for the symbol + * specified by src and slen, using ps to convert from multi-byte to wide + * character. Zero is returned if the symbol is not in an equivalence + * class. -1 is returned if there are wide character conversion errors, + * if there are any greater-than-8-bit characters or if a multi-byte symbol + * is greater or equal to COLLATE_STR_LEN in length. It is up to the calling + * routine to preserve the mbstate_t structure as needed. + */ +int +__collate_equiv_class(const char *src, size_t slen, mbstate_t *ps) +{ + wchar_t wname[COLLATE_STR_LEN]; + wchar_t w, *wp; + struct xlocale_collate *table; + size_t len, l; + int e; + + table = + (struct xlocale_collate *)__get_locale()->components[XLC_COLLATE]; + /* POSIX locale */ + if (table->__collate_load_error) + return (0); + for (wp = wname, len = 0; slen > 0; len++) { + l = mbrtowc(&w, src, slen, ps); + if (l == (size_t)-1 || l == (size_t)-2) + return (-1); + if (l == 0) + break; + if (len >= COLLATE_STR_LEN) + return (-1); + *wp++ = w; + src += l; + slen -= l; + } + if (len == 0) + return (-1); + if (len == 1) { + e = -1; + if (*wname <= UCHAR_MAX) + e = table->char_pri_table[*wname].pri[0]; + else if (table->info->large_count > 0) { + collate_large_t *match; + match = largesearch(table, *wname); + if (match) + e = match->pri.pri[0]; + } + if (e == 0) + return (IGNORE_EQUIV_CLASS); + return (e > 0 ? e : 0); + } + *wp = 0; + if (table->info->chain_count > 0) { + collate_chain_t *match; + int ll; + match = chainsearch(table, wname, &ll); + if (match) { + e = match->pri[0]; + if (e == 0) + return (IGNORE_EQUIV_CLASS); + return (e < 0 ? -e : e); + } + } + return (0); +} + + +/* + * __collate_equiv_match tries to match any single or multi-character symbol + * in equivalence class equiv_class in the multi-byte string specified by src + * and slen. If start is non-zero, it is taken to be the first (pre-converted) + * wide character. Subsequence wide characters, if needed, will use ps in + * the conversion. On a successful match, the length of the matched string + * is returned (including the start character). If dst is non-NULL, the + * matched wide-character string is copied to dst, a wide character array of + * length dlen (the results are not zero-terminated). If rlen is non-NULL, + * the number of character in src actually used is returned. Zero is + * returned by __collate_equiv_match if there is no match. (size_t)-1 is + * returned on error: if there were conversion errors or if dlen is too small + * to accept the results. On no match or error, ps is restored to its incoming + * state. + */ +size_t +__collate_equiv_match(int equiv_class, wchar_t *dst, size_t dlen, wchar_t start, + const char *src, size_t slen, mbstate_t *ps, size_t *rlen) +{ + wchar_t w; + size_t len, l, clen; + int i; + wchar_t buf[COLLATE_STR_LEN], *wp; + mbstate_t save; + const char *s = src; + struct xlocale_collate *table; + size_t sl = slen; + collate_chain_t *ch = NULL; + + table = + (struct xlocale_collate *)__get_locale()->components[XLC_COLLATE]; + /* POSIX locale */ + if (table->__collate_load_error) + return ((size_t)-1); + if (equiv_class == IGNORE_EQUIV_CLASS) + equiv_class = 0; + if (ps) + save = *ps; + wp = buf; + len = clen = 0; + if (start) { + *wp++ = start; + len = 1; + } + /* convert up to the max chain length */ + while (sl > 0 && len < table->info->chain_max_len) { + l = mbrtowc(&w, s, sl, ps); + if (l == (size_t)-1 || l == (size_t)-2 || l == 0) + break; + *wp++ = w; + s += l; + clen += l; + sl -= l; + len++; + } + *wp = 0; + if (len > 1 && (ch = chainsearch(table, buf, &i)) != NULL) { + int e = ch->pri[0]; + if (e < 0) + e = -e; + if (e == equiv_class) + goto found; + } + /* try single character */ + i = 1; + if (*buf <= UCHAR_MAX) { + if (equiv_class == table->char_pri_table[*buf].pri[0]) + goto found; + } else if (table->info->large_count > 0) { + collate_large_t *match; + match = largesearch(table, *buf); + if (match && equiv_class == match->pri.pri[0]) + goto found; + } + /* no match */ + if (ps) + *ps = save; + return (0); +found: + /* + * If we converted more than we used, restore to initial + * and reconvert up to what did match. + */ + if (i < len) { + len = i; + if (ps) + *ps = save; + if (start) + i--; + clen = 0; + while (i-- > 0) { + l = mbrtowc(&w, src, slen, ps); + src += l; + clen += l; + slen -= l; + } + } + if (dst) { + if (dlen < len) { + if (ps) + *ps = save; + return ((size_t)-1); + } + for (wp = buf; len > 0; len--) + *dst++ = *wp++; + } + if (rlen) + *rlen = clen; + return (len); +} diff --git a/lib/libc/locale/collate.h b/lib/libc/locale/collate.h index f157d8651899..64e0e6f2337d 100644 --- a/lib/libc/locale/collate.h +++ b/lib/libc/locale/collate.h @@ -38,6 +38,7 @@ #include <sys/types.h> #include <limits.h> +#include <wchar.h> #include "xlocale_private.h" /* @@ -65,6 +66,8 @@ #define DIRECTIVE_DIRECTION_MASK (DIRECTIVE_FORWARD | DIRECTIVE_BACKWARD) +#define IGNORE_EQUIV_CLASS 1 + /* * The collate file format is as follows: * @@ -85,6 +88,7 @@ typedef struct collate_info { uint8_t directive_count; uint8_t directive[COLL_WEIGHTS_MAX]; + uint8_t chain_max_len; /* In padding */ int32_t pri_count[COLL_WEIGHTS_MAX]; int32_t flags; int32_t chain_count; @@ -126,8 +130,13 @@ struct xlocale_collate { }; __BEGIN_DECLS -int __collate_load_tables(const char *); +size_t __collate_collating_symbol(wchar_t *, size_t, const char *, size_t, + mbstate_t *); +int __collate_equiv_class(const char *, size_t, mbstate_t *); int __collate_equiv_value(locale_t, const wchar_t *, size_t); +size_t __collate_equiv_match(int, wchar_t *, size_t, wchar_t, const char *, + size_t, mbstate_t *, size_t *); +int __collate_load_tables(const char *); void _collate_lookup(struct xlocale_collate *,const wchar_t *, int *, int *, int, const int **); int __collate_range_cmp(char, char); diff --git a/lib/libc/locale/euc.c b/lib/libc/locale/euc.c index c54a6ceceff2..c110fc61b375 100644 --- a/lib/libc/locale/euc.c +++ b/lib/libc/locale/euc.c @@ -426,7 +426,7 @@ _EUC_wcrtomb_impl(char * __restrict s, wchar_t wc, /* This first check excludes CS1, which is implicitly valid. */ if ((wc < 0xa100) || (wc > 0xffff)) { /* Check for valid CS2 or CS3 */ - nm = (wc >> ((len - 1) * 8)); + nm = (wc >> ((len - 1) * 8)) & 0xff; if (nm == cs2) { if (len != cs2width) { errno = EILSEQ; diff --git a/lib/libc/md/Makefile.inc b/lib/libc/md/Makefile.inc deleted file mode 100644 index 82c5f0670485..000000000000 --- a/lib/libc/md/Makefile.inc +++ /dev/null @@ -1,3 +0,0 @@ -.PATH: ${SRCTOP}/sys/kern - -SRCS+= md5c.c diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc index 558cf7be5771..7e9e5295c78c 100644 --- a/lib/libc/net/Makefile.inc +++ b/lib/libc/net/Makefile.inc @@ -1,7 +1,7 @@ # machine-independent net sources .PATH: ${LIBC_SRCTOP}/net -CONFS+= net/hosts net/hosts.equiv net/networks net/nsswitch.conf net/protocols +CONFS+= net/hosts net/hosts.equiv net/networks ${.OBJDIR}/nsswitch.conf net/protocols SRCS+= base64.c ether_addr.c eui64.c \ gai_strerror.c getaddrinfo.c \ gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \ @@ -141,7 +141,8 @@ MLINKS+=inet6_opt_init.3 inet6_opt_append.3 \ inet6_rthdr_space.3 inet6_rthdr_reverse.3 \ inet6_rthdr_space.3 inet6_rthdr_segments.3 MLINKS+=linkaddr.3 link_addr.3 \ - linkaddr.3 link_ntoa.3 + linkaddr.3 link_ntoa.3 \ + linkaddr.3 link_ntoa_r.3 MLINKS+=rcmd.3 iruserok.3 \ rcmd.3 iruserok_sa.3 \ rcmd.3 rcmd_af.3 \ @@ -176,9 +177,13 @@ MLINKS+=hesiod.3 hesiod_init.3 \ hesiod.3 hesiod_end.3 .endif -afterinstallconfig: modify-nsswitch-conf -modify-nsswitch-conf: .PHONY +all: ${.OBJDIR}/nsswitch.conf + +# Manually stage nsswitch.conf in the object directory so that we can edit it +# before installing. +${.OBJDIR}/nsswitch.conf: ${LIBC_SRCTOP}/net/nsswitch.conf + cp -f ${LIBC_SRCTOP}/net/nsswitch.conf ${.TARGET} .if ${MK_NIS} == "no" sed -i "" -e 's/.*_compat:/# &/' -e 's/compat$$/files/' \ - ${DESTDIR}/etc/nsswitch.conf + ${.TARGET} .endif diff --git a/lib/libc/net/Symbol.map b/lib/libc/net/Symbol.map index ebc3a56b549f..25e79b4e5807 100644 --- a/lib/libc/net/Symbol.map +++ b/lib/libc/net/Symbol.map @@ -138,6 +138,10 @@ FBSD_1.3 { sctp_sendv; }; +FBSD_1.8 { + link_ntoa_r; +}; + FBSDprivate_1.0 { _nsdispatch; _nsyyerror; /* generated from nslexer.l */ diff --git a/lib/libc/net/gai_strerror.c b/lib/libc/net/gai_strerror.c index 0d4303e76a73..23f58b763573 100644 --- a/lib/libc/net/gai_strerror.c +++ b/lib/libc/net/gai_strerror.c @@ -45,22 +45,22 @@ * Entries EAI_ADDRFAMILY (1) and EAI_NODATA (7) were omitted from RFC 3493, * but are or may be used as extensions or in old code. */ -static const char *ai_errlist[] = { - "Success", /* 0 */ - "Address family for hostname not supported", /* EAI_ADDRFAMILY */ - "Name could not be resolved at this time", /* EAI_AGAIN */ - "Flags parameter had an invalid value", /* EAI_BADFLAGS */ - "Non-recoverable failure in name resolution", /* EAI_FAIL */ - "Address family not recognized", /* EAI_FAMILY */ - "Memory allocation failure", /* EAI_MEMORY */ - "No address associated with hostname", /* EAI_NODATA*/ - "Name does not resolve", /* EAI_NONAME */ - "Service was not recognized for socket type", /* EAI_SERVICE */ - "Intended socket type was not recognized", /* EAI_SOCKTYPE */ - "System error returned in errno", /* EAI_SYSTEM */ - "Invalid value for hints", /* EAI_BADHINTS */ - "Resolved protocol is unknown", /* EAI_PROTOCOL */ - "Argument buffer overflow" /* EAI_OVERFLOW */ +static const char *const ai_errlist[] = { + [0] = "Success", + [EAI_ADDRFAMILY] = "Address family for hostname not supported", + [EAI_AGAIN] = "Name could not be resolved at this time", + [EAI_BADFLAGS] = "Flags parameter had an invalid value", + [EAI_FAIL] = "Non-recoverable failure in name resolution", + [EAI_FAMILY] = "Address family not recognized", + [EAI_MEMORY] = "Memory allocation failure", + [EAI_NODATA] = "No address associated with hostname", + [EAI_NONAME] = "Name does not resolve", + [EAI_SERVICE] = "Service was not recognized for socket type", + [EAI_SOCKTYPE] = "Intended socket type was not recognized", + [EAI_SYSTEM] = "System error returned in errno", + [EAI_BADHINTS] = "Invalid value for hints", + [EAI_PROTOCOL] = "Resolved protocol is unknown", + [EAI_OVERFLOW] = "Argument buffer overflow", }; #if defined(NLS) @@ -72,7 +72,7 @@ static int gai_keycreated = 0; static void gai_keycreate(void) { - gai_keycreated = (thr_keycreate(&gai_key, free) == 0); + gai_keycreated = thr_keycreate(&gai_key, free) == 0; } #endif @@ -82,7 +82,9 @@ gai_strerror(int ecode) #if defined(NLS) nl_catd catd; char *buf; + int saved_errno; + saved_errno = errno; if (thr_main() != 0) buf = gai_buf; else { @@ -110,11 +112,13 @@ gai_strerror(int ecode) strlcpy(buf, catgets(catd, 3, NL_MSGMAX, "Unknown error"), sizeof(gai_buf)); catclose(catd); - return buf; + errno = saved_errno; + return (buf); thr_err: + errno = saved_errno; #endif if (ecode >= 0 && ecode < EAI_MAX) - return ai_errlist[ecode]; - return "Unknown error"; + return (ai_errlist[ecode]); + return ("Unknown error"); } diff --git a/lib/libc/net/gethostbydns.c b/lib/libc/net/gethostbydns.c index b29fa1cdd845..216fc9bcf9a4 100644 --- a/lib/libc/net/gethostbydns.c +++ b/lib/libc/net/gethostbydns.c @@ -74,8 +74,10 @@ #define SPRINTF(x) ((size_t)sprintf x) +#ifdef DEBUG static const char AskedForGot[] = "gethostby*.gethostanswer: asked for \"%s\", got \"%s\""; +#endif #ifdef RESOLVSORT static void addrsort(char **, int, res_state); @@ -299,8 +301,10 @@ gethostanswer(const querybuf *answer, int anslen, const char *qname, int qtype, switch (type) { case T_PTR: if (strcasecmp(tname, bp) != 0) { +#ifdef DEBUG syslog(LOG_NOTICE|LOG_AUTH, AskedForGot, qname, bp); +#endif cp += n; continue; /* XXX - had_error++ ? */ } @@ -347,8 +351,10 @@ gethostanswer(const querybuf *answer, int anslen, const char *qname, int qtype, case T_A: case T_AAAA: if (strcasecmp(he->h_name, bp) != 0) { +#ifdef DEBUG syslog(LOG_NOTICE|LOG_AUTH, AskedForGot, he->h_name, bp); +#endif cp += n; continue; /* XXX - had_error++ ? */ } diff --git a/lib/libc/net/linkaddr.3 b/lib/libc/net/linkaddr.3 index ee3ea3056c71..38ee6a0aedea 100644 --- a/lib/libc/net/linkaddr.3 +++ b/lib/libc/net/linkaddr.3 @@ -28,12 +28,13 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 28, 2007 +.Dd May 9, 2025 .Dt LINK_ADDR 3 .Os .Sh NAME .Nm link_addr , -.Nm link_ntoa +.Nm link_ntoa , +.Nm link_ntoa_r .Nd elementary address specification routines for link level access .Sh LIBRARY .Lb libc @@ -41,16 +42,29 @@ .In sys/types.h .In sys/socket.h .In net/if_dl.h -.Ft void +.Ft int .Fn link_addr "const char *addr" "struct sockaddr_dl *sdl" .Ft char * .Fn link_ntoa "const struct sockaddr_dl *sdl" +.Ft int +.Fn link_ntoa_r "const struct sockaddr_dl *sdl" "char *obuf" "size_t *buflen" .Sh DESCRIPTION The routine .Fn link_addr -interprets character strings representing -link-level addresses, returning binary information suitable -for use in system calls. +parses a character string +.Fa addr +representing a link-level address, +and stores the resulting address in the structure pointed to by +.Fa sdl . +A link-level address consists of an optional interface name, followed by +a colon (which is required in all cases), followed by an address +consisting of either a string of hexadecimal digits, or a series of +hexadecimal octets separated by one of the characters +.Sq "." , +.Sq ":" , +or +.Sq - . +.Pp The routine .Fn link_ntoa takes @@ -60,9 +74,34 @@ address and returns an string representing some of the information present, including the link level address itself, and the interface name or number, if present. +The returned string is stored in a static buffer. This facility is experimental and is still subject to change. .Pp +The routine +.Fn link_ntoa_r +behaves like +.Fn link_ntoa , +except the string is placed in the provided buffer instead of a static +buffer. +The caller should initialize +.Fa buflen +to the number of bytes available in +.Fa obuf . +On return, +.Fa buflen +is set to the actual number of bytes required for the output buffer, +including the NUL terminator. +If +.Fa obuf +is NULL, then +.Fa buflen +is set as described, but nothing is written. +This may be used to determine the required length of the buffer before +calling +.Fn link_ntoa_r +a second time. +.Pp For .Fn link_addr , the string @@ -94,12 +133,21 @@ The .Fn link_ntoa function always returns a null terminated string. +.Pp +The +.Fn link_ntoa_r +function returns 0 on success, or -1 if the provided buffer was not +large enough; in the latter case, the contents of the buffer are +indeterminate, but a trailing NUL will always be written if the buffer +was at least one byte in size. +.Pp The .Fn link_addr -function -has no return value. -(See -.Sx BUGS . ) +function returns 0 on success. +If the address did not appear to be a valid link-level address, -1 is +returned and +.Va errno +is set to indicate the error. .Sh SEE ALSO .Xr getnameinfo 3 .Sh HISTORY @@ -109,15 +157,14 @@ and .Fn link_ntoa functions appeared in .Bx 4.3 Reno . +The +.Fn link_ntoa_r +function appeared in +.Fx 15.0 . .Sh BUGS The returned values for link_ntoa reside in a static memory area. .Pp -The function -.Fn link_addr -should diagnose improperly formed input, and there should be an unambiguous -way to recognize this. -.Pp If the .Va sdl_len field of the link socket address diff --git a/lib/libc/net/linkaddr.c b/lib/libc/net/linkaddr.c index dedc910b9474..5be4c0a7a43e 100644 --- a/lib/libc/net/linkaddr.c +++ b/lib/libc/net/linkaddr.c @@ -31,135 +31,261 @@ #include <sys/types.h> #include <sys/socket.h> + #include <net/if.h> #include <net/if_dl.h> + +#include <assert.h> +#include <errno.h> +#include <stdint.h> #include <string.h> -/* States*/ -#define NAMING 0 -#define GOTONE 1 -#define GOTTWO 2 -#define RESET 3 -/* Inputs */ -#define DIGIT (4*0) -#define END (4*1) -#define DELIM (4*2) -#define LETTER (4*3) - -void +int link_addr(const char *addr, struct sockaddr_dl *sdl) { char *cp = sdl->sdl_data; char *cplim = sdl->sdl_len + (char *)sdl; - int byte = 0, state = NAMING, new; + const char *nptr; + size_t newsize; + int error = 0; + char delim = 0; + /* Initialise the sdl to zero, except for sdl_len. */ bzero((char *)&sdl->sdl_family, sdl->sdl_len - 1); sdl->sdl_family = AF_LINK; - do { - state &= ~LETTER; - if ((*addr >= '0') && (*addr <= '9')) { - new = *addr - '0'; - } else if ((*addr >= 'a') && (*addr <= 'f')) { - new = *addr - 'a' + 10; - } else if ((*addr >= 'A') && (*addr <= 'F')) { - new = *addr - 'A' + 10; - } else if (*addr == 0) { - state |= END; - } else if (state == NAMING && - (((*addr >= 'A') && (*addr <= 'Z')) || - ((*addr >= 'a') && (*addr <= 'z')))) - state |= LETTER; - else - state |= DELIM; - addr++; - switch (state /* | INPUT */) { - case NAMING | DIGIT: - case NAMING | LETTER: - *cp++ = addr[-1]; - continue; - case NAMING | DELIM: - state = RESET; - sdl->sdl_nlen = cp - sdl->sdl_data; - continue; - case GOTTWO | DIGIT: - *cp++ = byte; - /* FALLTHROUGH */ - case RESET | DIGIT: - state = GOTONE; - byte = new; - continue; - case GOTONE | DIGIT: - state = GOTTWO; - byte = new + (byte << 4); - continue; - default: /* | DELIM */ - state = RESET; - *cp++ = byte; - byte = 0; - continue; - case GOTONE | END: - case GOTTWO | END: - *cp++ = byte; - /* FALLTHROUGH */ - case RESET | END: + + /* + * Everything up to the first ':' is the interface name. Usually the + * ':' should always be present even if there's no interface name, but + * since this interface was poorly specified in the past, accept a + * missing colon as meaning no interface name. + */ + if ((nptr = strchr(addr, ':')) != NULL) { + size_t namelen = nptr - addr; + + /* Ensure the sdl is large enough to store the name. */ + if (namelen > cplim - cp) { + errno = ENOSPC; + return (-1); + } + + memcpy(cp, addr, namelen); + cp += namelen; + sdl->sdl_nlen = namelen; + /* Skip the interface name and the colon. */ + addr += namelen + 1; + } + + /* + * The remainder of the string should be hex digits representing the + * address, with optional delimiters. Each two hex digits form one + * octet, but octet output can be forced using a delimiter, so we accept + * a long string of hex digits, or a mix of delimited and undelimited + * digits like "1122.3344.5566", or delimited one- or two-digit octets + * like "1.22.3". + * + * If anything fails at this point, exit the loop so we set sdl_alen and + * sdl_len based on whatever we did manage to parse. This preserves + * compatibility with the 4.3BSD version of link_addr, which had no way + * to indicate an error and would just return. + */ +#define DIGIT(c) \ + (((c) >= '0' && (c) <= '9') ? ((c) - '0') \ + : ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) \ + : ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) \ + : (-1)) +#define ISDELIM(c) (((c) == '.' || (c) == ':' || (c) == '-') && \ + (delim == 0 || delim == (c))) + + for (;;) { + int digit, digit2; + + /* + * Treat any leading delimiters as empty bytes. This supports + * the (somewhat obsolete) form of Ethernet addresses with empty + * octets, e.g. "1::3:4:5:6". + */ + while (ISDELIM(*addr) && cp < cplim) { + delim = *addr++; + *cp++ = 0; + } + + /* Did we reach the end of the string? */ + if (*addr == '\0') + break; + + /* + * If not, the next character must be a digit, so make sure we + * have room for at least one more octet. + */ + + if (cp >= cplim) { + error = ENOSPC; + break; + } + + if ((digit = DIGIT(*addr)) == -1) { + error = EINVAL; break; } - break; - } while (cp < cplim); + + ++addr; + + /* If the next character is another digit, consume it. */ + if ((digit2 = DIGIT(*addr)) != -1) { + digit = (digit << 4) | digit2; + ++addr; + } + + if (ISDELIM(*addr)) { + /* + * If the digit is followed by a delimiter, write it + * and consume the delimiter. + */ + delim = *addr++; + *cp++ = digit; + } else if (DIGIT(*addr) != -1) { + /* + * If two digits are followed by a third digit, treat + * the two digits we have as a single octet and + * continue. + */ + *cp++ = digit; + } else if (*addr == '\0') { + /* If the digit is followed by EOS, we're done. */ + *cp++ = digit; + break; + } else { + /* Otherwise, the input was invalid. */ + error = EINVAL; + break; + } + } +#undef DIGIT +#undef ISDELIM + + /* How many bytes did we write to the address? */ sdl->sdl_alen = cp - LLADDR(sdl); - new = cp - (char *)sdl; - if (new > sizeof(*sdl)) - sdl->sdl_len = new; - return; + + /* + * The user might have given us an sdl which is larger than sizeof(sdl); + * in that case, record the actual size of the new sdl. + */ + newsize = cp - (char *)sdl; + if (newsize > sizeof(*sdl)) + sdl->sdl_len = (u_char)newsize; + + if (error == 0) + return (0); + + errno = error; + return (-1); } -static const char hexlist[] = "0123456789abcdef"; char * link_ntoa(const struct sockaddr_dl *sdl) { static char obuf[64]; + size_t buflen; _Static_assert(sizeof(obuf) >= IFNAMSIZ + 20, "obuf is too small"); + + /* + * Ignoring the return value of link_ntoa_r() is safe here because it + * always writes the terminating NUL. This preserves the traditional + * behaviour of link_ntoa(). + */ + buflen = sizeof(obuf); + (void)link_ntoa_r(sdl, obuf, &buflen); + return obuf; +} + +int +link_ntoa_r(const struct sockaddr_dl *sdl, char *obuf, size_t *buflen) +{ + static const char hexlist[] = "0123456789abcdef"; char *out; const u_char *in, *inlim; int namelen, i, rem; + size_t needed; - namelen = (sdl->sdl_nlen <= IFNAMSIZ) ? sdl->sdl_nlen : IFNAMSIZ; + assert(sdl); + assert(buflen); + /* obuf may be null */ + needed = 1; /* 1 for the NUL */ out = obuf; - rem = sizeof(obuf); + if (obuf) + rem = *buflen; + else + rem = 0; + +/* + * Check if at least n bytes are available in the output buffer, plus 1 for the + * trailing NUL. If not, set rem = 0 so we stop writing. + * Either way, increment needed by the amount we would have written. + */ +#define CHECK(n) do { \ + if ((SIZE_MAX - (n)) >= needed) \ + needed += (n); \ + if (rem >= ((n) + 1)) \ + rem -= (n); \ + else \ + rem = 0; \ + } while (0) + +/* + * Write the char c to the output buffer, unless the buffer is full. + * Note that if obuf is NULL, rem is always zero. + */ +#define OUT(c) do { \ + if (rem > 0) \ + *out++ = (c); \ + } while (0) + + namelen = (sdl->sdl_nlen <= IFNAMSIZ) ? sdl->sdl_nlen : IFNAMSIZ; if (namelen > 0) { - bcopy(sdl->sdl_data, out, namelen); - out += namelen; - rem -= namelen; + CHECK(namelen); + if (rem > 0) { + bcopy(sdl->sdl_data, out, namelen); + out += namelen; + } + if (sdl->sdl_alen > 0) { - *out++ = ':'; - rem--; + CHECK(1); + OUT(':'); } } - in = (const u_char *)sdl->sdl_data + sdl->sdl_nlen; + in = (const u_char *)LLADDR(sdl); inlim = in + sdl->sdl_alen; - while (in < inlim && rem > 1) { - if (in != (const u_char *)sdl->sdl_data + sdl->sdl_nlen) { - *out++ = '.'; - rem--; + while (in < inlim) { + if (in != (const u_char *)LLADDR(sdl)) { + CHECK(1); + OUT('.'); } i = *in++; if (i > 0xf) { - if (rem < 3) - break; - *out++ = hexlist[i >> 4]; - *out++ = hexlist[i & 0xf]; - rem -= 2; + CHECK(2); + OUT(hexlist[i >> 4]); + OUT(hexlist[i & 0xf]); } else { - if (rem < 2) - break; - *out++ = hexlist[i]; - rem--; + CHECK(1); + OUT(hexlist[i]); } } - *out = 0; - return (obuf); + +#undef CHECK +#undef OUT + + /* + * We always leave enough room for the NUL if possible, but the user + * might have passed a NULL or zero-length buffer. + */ + if (out && *buflen) + *out = '\0'; + + *buflen = needed; + return ((rem > 0) ? 0 : -1); } diff --git a/lib/libc/net/res_config.h b/lib/libc/net/res_config.h index f049d6817b7a..39a1b5f1486f 100644 --- a/lib/libc/net/res_config.h +++ b/lib/libc/net/res_config.h @@ -1,5 +1,5 @@ -#define DEBUG 1 /* enable debugging code (needed for dig) */ +//#define DEBUG /* enable debugging code */ #define RESOLVSORT /* allow sorting of addresses in gethostbyname */ -#undef SUNSECURITY /* verify gethostbyaddr() calls - WE DON'T NEED IT */ +//#define SUNSECURITY /* verify gethostbyaddr() calls */ #define MULTI_PTRS_ARE_ALIASES 1 /* fold multiple PTR records into aliases */ diff --git a/lib/libc/nls/Makefile.inc b/lib/libc/nls/Makefile.inc index f26e04c187a5..c211026aba72 100644 --- a/lib/libc/nls/Makefile.inc +++ b/lib/libc/nls/Makefile.inc @@ -12,6 +12,11 @@ MAN+= catclose.3 catgets.3 catopen.3 # for translators. NLSNAME= libc +# We don't want libc's NLS catalogues to be installed in the clibs package. +# Put them in locales instead, since anyone interested in NLS will have +# that installed. +NLSPACKAGE= locales + NLS+= be_BY.UTF-8 NLS+= ca_ES.ISO8859-1 NLS+= de_DE.ISO8859-1 diff --git a/lib/libc/powerpc/static_tls.h b/lib/libc/powerpc/static_tls.h deleted file mode 100644 index 9ae38c71a515..000000000000 --- a/lib/libc/powerpc/static_tls.h +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by Konstantin Belousov <kib@FreeBSD.org> - * under sponsorship from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _LIBC_POWERPC_STATIC_TLS_H -#define _LIBC_POWERPC_STATIC_TLS_H - -static __inline uintptr_t -_libc_get_static_tls_base(size_t offset) -{ - uintptr_t tlsbase; - - __asm __volatile("mr %0,2" : "=r"(tlsbase)); - tlsbase += offset - 0x7008; - return (tlsbase); -} - -#endif diff --git a/lib/libc/powerpc64/gen/_ctx_start.S b/lib/libc/powerpc64/gen/_ctx_start.S index c2f8abfd6486..98225f9c1138 100644 --- a/lib/libc/powerpc64/gen/_ctx_start.S +++ b/lib/libc/powerpc64/gen/_ctx_start.S @@ -34,6 +34,16 @@ ld %r2,8(%r14) ld %r14,0(%r14) #else + /* + * The stack frame was already set up in makecontext(), + * so we can safely use the guaranteed fields here. + * + * Note we do step on the allocated stack frame's TOC, + * but since we never return from this function (i.e. + * never restore the stack frame) this should be safe. + */ + std %r2,24(%r1) /* save TOC */ + /* Load global entry point */ mr %r12,%r14 #endif @@ -41,6 +51,10 @@ blrl /* branch to start function */ mr %r3,%r15 /* pass pointer to ucontext as argument */ nop +#if defined(_CALL_ELF) && _CALL_ELF != 1 + /* Restore TOC */ + ld %r2,24(%r1) +#endif bl CNAME(_ctx_done) /* branch to ctxt completion func */ /* * we should never return from the diff --git a/lib/libc/powerpc64/gen/makecontext.c b/lib/libc/powerpc64/gen/makecontext.c index 75c2d40bdd60..9e3a976fa1bd 100644 --- a/lib/libc/powerpc64/gen/makecontext.c +++ b/lib/libc/powerpc64/gen/makecontext.c @@ -78,7 +78,7 @@ __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) */ stackargs = (argc > 8) ? argc - 8 : 0; sp = (char *) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size - - sizeof(uintptr_t)*(stackargs + 2); + - sizeof(uintptr_t)*(stackargs + 6); sp = (char *)((uintptr_t)sp & ~0x1f); mc = &ucp->uc_mcontext; @@ -119,6 +119,7 @@ __makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...) mc->mc_srr0 = *(uintptr_t *)_ctx_start; #else mc->mc_srr0 = (uintptr_t) _ctx_start; + mc->mc_gpr[12] = (uintptr_t) _ctx_start;/* required for prologue */ #endif mc->mc_gpr[1] = (uintptr_t) sp; /* new stack pointer */ mc->mc_gpr[14] = (uintptr_t) start; /* r14 <- start */ diff --git a/lib/libc/powerpc64/static_tls.h b/lib/libc/powerpc64/static_tls.h deleted file mode 100644 index 014b8a931453..000000000000 --- a/lib/libc/powerpc64/static_tls.h +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by Konstantin Belousov <kib@FreeBSD.org> - * under sponsorship from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _LIBC_POWERPC64_STATIC_TLS_H -#define _LIBC_POWERPC64_STATIC_TLS_H - -static __inline uintptr_t -_libc_get_static_tls_base(size_t offset) -{ - uintptr_t tlsbase; - - __asm __volatile("mr %0,13" : "=r"(tlsbase)); - tlsbase += offset - 0x7010; - return (tlsbase); -} - -#endif diff --git a/lib/libc/regex/regcomp.c b/lib/libc/regex/regcomp.c index f34dc322d0bb..aebea2b02435 100644 --- a/lib/libc/regex/regcomp.c +++ b/lib/libc/regex/regcomp.c @@ -1183,6 +1183,7 @@ p_b_pseudoclass(struct parse *p, char c) { /* PASSTHROUGH */ case 'w': p_b_cclass_named(p, cs, "alnum"); + CHadd(p, cs, '_'); break; case 'S': cs->invert = 1; diff --git a/lib/libc/resolv/Symbol.map b/lib/libc/resolv/Symbol.map index 6b9c43298fb5..26daecbe2eff 100644 --- a/lib/libc/resolv/Symbol.map +++ b/lib/libc/resolv/Symbol.map @@ -103,6 +103,5 @@ FBSD_1.0 { }; FBSD_1.4 { - __res_rndinit; __res_nrandomid; }; diff --git a/lib/libc/resolv/res_debug.h b/lib/libc/resolv/res_debug.h index dd048116fb49..ccae03e625aa 100644 --- a/lib/libc/resolv/res_debug.h +++ b/lib/libc/resolv/res_debug.h @@ -23,7 +23,7 @@ #ifndef DEBUG # define Dprint(cond, args) /*empty*/ # define DprintQ(cond, args, query, size) /*empty*/ -# define Aerror(statp, file, string, error, address) /*empty*/ +# define Aerror(statp, file, string, error, address, alen) /*empty*/ # define Perror(statp, file, string, error) /*empty*/ #else # define Dprint(cond, args) if (cond) {fprintf args;} else {} diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c index 70d6bc6d3bf2..5a2fce013c8c 100644 --- a/lib/libc/resolv/res_init.c +++ b/lib/libc/resolv/res_init.c @@ -86,19 +86,6 @@ #include <unistd.h> #include <netdb.h> -#ifndef HAVE_MD5 -# include "../dst/md5.h" -#else -# ifdef SOLARIS2 -# include <sys/md5.h> -# elif _LIBC -# include <md5.h> -# endif -#endif -#ifndef _MD5_H_ -# define _MD5_H_ 1 /*%< make sure we do not include rsaref md5.h file */ -#endif - #include "un-namespace.h" #include "port_after.h" @@ -108,12 +95,6 @@ #include "res_private.h" -/*% Options. Should all be left alone. */ -#define RESOLVSORT -#ifndef DEBUG -#define DEBUG -#endif - #ifdef SOLARIS2 #include <sys/systeminfo.h> #endif @@ -190,8 +171,6 @@ __res_vinit(res_state statp, int preinit) { statp->options = RES_DEFAULT; } - statp->_rnd = malloc(16); - res_rndinit(statp); statp->id = res_nrandomid(statp); memset(u, 0, sizeof(u)); @@ -739,48 +718,18 @@ net_mask(struct in_addr in) /*!< XXX - should really use system's version of th } #endif -static u_char srnd[16]; - void -res_rndinit(res_state statp) +freebsd15_res_rndinit(res_state statp) { - struct timeval now; - u_int32_t u32; - u_int16_t u16; - u_char *rnd = statp->_rnd == NULL ? srnd : statp->_rnd; - - gettimeofday(&now, NULL); - u32 = now.tv_sec; - memcpy(rnd, &u32, 4); - u32 = now.tv_usec; - memcpy(rnd + 4, &u32, 4); - u32 += now.tv_sec; - memcpy(rnd + 8, &u32, 4); - u16 = getpid(); - memcpy(rnd + 12, &u16, 2); + (void)statp; } +__sym_compat(__res_rndinit, freebsd15_res_rndinit, FBSD_1.4); u_int res_nrandomid(res_state statp) { - struct timeval now; - u_int16_t u16; - MD5_CTX ctx; - u_char *rnd = statp->_rnd == NULL ? srnd : statp->_rnd; - - gettimeofday(&now, NULL); - u16 = (u_int16_t) (now.tv_sec ^ now.tv_usec); - memcpy(rnd + 14, &u16, 2); -#ifndef HAVE_MD5 - MD5_Init(&ctx); - MD5_Update(&ctx, rnd, 16); - MD5_Final(rnd, &ctx); -#else - MD5Init(&ctx); - MD5Update(&ctx, rnd, 16); - MD5Final(rnd, &ctx); -#endif - memcpy(&u16, rnd + 14, 2); - return ((u_int) u16); + (void) statp; + + return ((u_int)(arc4random() & 0xffff)); } /*% @@ -814,10 +763,6 @@ res_ndestroy(res_state statp) { free(statp->_u._ext.ext); statp->_u._ext.ext = NULL; } - if (statp->_rnd != NULL) { - free(statp->_rnd); - statp->_rnd = NULL; - } statp->options &= ~RES_INIT; } diff --git a/lib/libc/resolv/res_mkquery.c b/lib/libc/resolv/res_mkquery.c index 0c15def5d117..f6767a92375c 100644 --- a/lib/libc/resolv/res_mkquery.c +++ b/lib/libc/resolv/res_mkquery.c @@ -76,11 +76,6 @@ #include <string.h> #include "port_after.h" -/* Options. Leave them on. */ -#ifndef DEBUG -#define DEBUG -#endif - extern const char *_res_opcodes[]; /*% diff --git a/lib/libc/resolv/res_mkupdate.c b/lib/libc/resolv/res_mkupdate.c index e5a3cb702cda..3f595dc4ec08 100644 --- a/lib/libc/resolv/res_mkupdate.c +++ b/lib/libc/resolv/res_mkupdate.c @@ -48,10 +48,6 @@ #include "port_after.h" -/* Options. Leave them on. */ -#ifndef DEBUG -#define DEBUG -#endif #define MAXPORT 1024 static int getnum_str(u_char **, u_char *); diff --git a/lib/libc/resolv/res_query.c b/lib/libc/resolv/res_query.c index e9c628ad8d47..f26d59e522b4 100644 --- a/lib/libc/resolv/res_query.c +++ b/lib/libc/resolv/res_query.c @@ -81,11 +81,6 @@ #include <unistd.h> #include "port_after.h" -/* Options. Leave them on. */ -#ifndef DEBUG -#define DEBUG -#endif - #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ #else diff --git a/lib/libc/resolv/res_send.c b/lib/libc/resolv/res_send.c index 3fb627b83d55..08c3aed7f934 100644 --- a/lib/libc/resolv/res_send.c +++ b/lib/libc/resolv/res_send.c @@ -112,10 +112,6 @@ #include "un-namespace.h" -/* Options. Leave them on. */ -#ifndef DEBUG -#define DEBUG -#endif #include "res_debug.h" #include "res_private.h" @@ -138,15 +134,12 @@ static int send_dg(res_state, const u_char *, int, u_char *, int, int *, int, int, int *, int *); +#ifdef DEBUG static void Aerror(const res_state, FILE *, const char *, int, const struct sockaddr *, int); static void Perror(const res_state, FILE *, const char *, int); -static int sock_eq(struct sockaddr *, struct sockaddr *); -#if defined(NEED_PSELECT) && !defined(USE_POLL) && !defined(USE_KQUEUE) -static int pselect(int, void *, void *, void *, - struct timespec *, - const sigset_t *); #endif +static int sock_eq(struct sockaddr *, struct sockaddr *); void res_pquery(const res_state, const u_char *, int, FILE *); static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; @@ -302,7 +295,9 @@ res_nsend(res_state statp, #ifdef USE_KQUEUE int kq; #endif +#ifdef DEBUG char abuf[NI_MAXHOST]; +#endif /* No name servers or res_init() failure */ if (statp->nscount == 0 || EXT(statp).ext == NULL) { @@ -418,10 +413,10 @@ res_nsend(res_state statp, */ for (tries = 0; tries < statp->retry; tries++) { for (ns = 0; ns < statp->nscount; ns++) { - struct sockaddr *nsap; - int nsaplen; - nsap = get_nsaddr(statp, ns); - nsaplen = get_salen(nsap); + struct sockaddr *nsap = get_nsaddr(statp, ns); +#ifdef DEBUG + int nsaplen = get_salen(nsap); +#endif statp->_flags &= ~RES_F_LASTMASK; statp->_flags |= (ns << RES_F_LASTSHIFT); same_ns: @@ -1088,6 +1083,7 @@ send_dg(res_state statp, return (resplen); } +#ifdef DEBUG static void Aerror(const res_state statp, FILE *file, const char *string, int error, const struct sockaddr *address, int alen) @@ -1119,6 +1115,7 @@ Perror(const res_state statp, FILE *file, const char *string, int error) { string, strerror(error)); errno = save; } +#endif static int sock_eq(struct sockaddr *a, struct sockaddr *b) { @@ -1145,29 +1142,3 @@ sock_eq(struct sockaddr *a, struct sockaddr *b) { return 0; } } - -#if defined(NEED_PSELECT) && !defined(USE_POLL) && !defined(USE_KQUEUE) -/* XXX needs to move to the porting library. */ -static int -pselect(int nfds, void *rfds, void *wfds, void *efds, - struct timespec *tsp, const sigset_t *sigmask) -{ - struct timeval tv, *tvp; - sigset_t sigs; - int n; - - if (tsp) { - tvp = &tv; - tv = evTimeVal(*tsp); - } else - tvp = NULL; - if (sigmask) - sigprocmask(SIG_SETMASK, sigmask, &sigs); - n = select(nfds, rfds, wfds, efds, tvp); - if (sigmask) - sigprocmask(SIG_SETMASK, &sigs, NULL); - if (tsp) - *tsp = evTimeSpec(tv); - return (n); -} -#endif diff --git a/lib/libc/riscv/static_tls.h b/lib/libc/riscv/static_tls.h deleted file mode 100644 index ce9fa23338a6..000000000000 --- a/lib/libc/riscv/static_tls.h +++ /dev/null @@ -1,44 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by Konstantin Belousov <kib@FreeBSD.org> - * under sponsorship from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _LIBC_RISCV_STATIC_TLS_H -#define _LIBC_RISCV_STATIC_TLS_H - -static __inline uintptr_t -_libc_get_static_tls_base(size_t offset) -{ - uintptr_t tlsbase; - - __asm __volatile("mv %0, tp" : "=r"(tlsbase)); - tlsbase += offset; - return (tlsbase); -} - -#endif diff --git a/lib/libc/rpc/rpc_generic.c b/lib/libc/rpc/rpc_generic.c index 0e563f6a5996..8019f2d8f236 100644 --- a/lib/libc/rpc/rpc_generic.c +++ b/lib/libc/rpc/rpc_generic.c @@ -610,6 +610,10 @@ __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf) return NULL; break; #endif + case AF_NETLINK: + if (asprintf(&ret, "%s", (char *)nbuf->buf) < 0) + return NULL; + break; case AF_LOCAL: sun = nbuf->buf; if (asprintf(&ret, "%.*s", (int)(sun->sun_len - diff --git a/lib/libc/rpc/rpc_secure.3 b/lib/libc/rpc/rpc_secure.3 index 11b871b5c99d..ce59bba7115f 100644 --- a/lib/libc/rpc/rpc_secure.3 +++ b/lib/libc/rpc/rpc_secure.3 @@ -266,8 +266,7 @@ Inverse of .Fn netname2user . .Sh SEE ALSO .Xr rpc 3 , -.Xr xdr 3 , -.Xr keyserv 8 +.Xr xdr 3 .Pp The following manuals: .Rs diff --git a/lib/libc/rpc/svc_nl.c b/lib/libc/rpc/svc_nl.c index 5cce44d9d98f..cff364063c5c 100644 --- a/lib/libc/rpc/svc_nl.c +++ b/lib/libc/rpc/svc_nl.c @@ -92,7 +92,7 @@ svc_nl_create(const char *service) .nl_family = PF_NETLINK, }; struct nl_softc *sc; - SVCXPRT *xprt; + SVCXPRT *xprt = NULL; void *buf = NULL; uint16_t family; ssize_t len = 1024; @@ -133,6 +133,7 @@ svc_nl_create(const char *service) return (xprt); fail: + free(xprt); free(buf); snl_free(&sc->snl); free(sc); diff --git a/lib/libc/stdio/Symbol.map b/lib/libc/stdio/Symbol.map index d491b83c11fb..e44ea6554d46 100644 --- a/lib/libc/stdio/Symbol.map +++ b/lib/libc/stdio/Symbol.map @@ -44,7 +44,6 @@ FBSD_1.0 { fwscanf; getc; getchar; - gets; getw; getwc; getwchar; diff --git a/lib/libc/stdio/fdopen.c b/lib/libc/stdio/fdopen.c index a0d7b71df782..49ec97eda39d 100644 --- a/lib/libc/stdio/fdopen.c +++ b/lib/libc/stdio/fdopen.c @@ -46,7 +46,7 @@ FILE * fdopen(int fd, const char *mode) { FILE *fp; - int flags, oflags, fdflags, tmp; + int flags, oflags, fdflags, rc, tmp; /* * File descriptors are a full int, but _file is only a short. @@ -76,9 +76,19 @@ fdopen(int fd, const char *mode) if ((fp = __sfp()) == NULL) return (NULL); - if ((oflags & O_CLOEXEC) && _fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { - fp->_flags = 0; - return (NULL); + if ((oflags & O_CLOEXEC) != 0) { + tmp = _fcntl(fd, F_GETFD, 0); + if (tmp == -1) { + fp->_flags = 0; + return (NULL); + } + if ((tmp & FD_CLOEXEC) == 0) { + rc = _fcntl(fd, F_SETFD, tmp | FD_CLOEXEC); + if (rc == -1) { + fp->_flags = 0; + return (NULL); + } + } } fp->_flags = flags; diff --git a/lib/libc/stdio/freopen.c b/lib/libc/stdio/freopen.c index f0732b6d6741..048fd30b3193 100644 --- a/lib/libc/stdio/freopen.c +++ b/lib/libc/stdio/freopen.c @@ -55,7 +55,7 @@ freopen(const char * __restrict file, const char * __restrict mode, FILE * __restrict fp) { int f; - int dflags, flags, isopen, oflags, sverrno, wantfd; + int dflags, fdflags, flags, isopen, oflags, sverrno, wantfd; if ((flags = __sflags(mode, &oflags)) == 0) { sverrno = errno; @@ -113,8 +113,12 @@ freopen(const char * __restrict file, const char * __restrict mode, (void) ftruncate(fp->_file, (off_t)0); if (!(oflags & O_APPEND)) (void) _sseek(fp, (fpos_t)0, SEEK_SET); - if (oflags & O_CLOEXEC) - (void) _fcntl(fp->_file, F_SETFD, FD_CLOEXEC); + if ((oflags & O_CLOEXEC) != 0) { + fdflags = _fcntl(fp->_file, F_GETFD, 0); + if (fdflags != -1 && (fdflags & FD_CLOEXEC) == 0) + (void) _fcntl(fp->_file, F_SETFD, + fdflags | FD_CLOEXEC); + } f = fp->_file; isopen = 0; wantfd = -1; diff --git a/lib/libc/stdio/mktemp.3 b/lib/libc/stdio/mktemp.3 index 2b8b2d6c9e44..8d38dd2cd57e 100644 --- a/lib/libc/stdio/mktemp.3 +++ b/lib/libc/stdio/mktemp.3 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 29, 2019 +.Dd July 07, 2025 .Dt MKTEMP 3 .Os .Sh NAME @@ -101,9 +101,10 @@ The permitted flags are .Dv O_DIRECT , .Dv O_SHLOCK , .Dv O_EXLOCK , -.Dv O_SYNC +.Dv O_SYNC , +.Dv O_CLOEXEC and -.Dv O_CLOEXEC . +.Dv O_CLOFORK . .Pp The .Fn mkstemps diff --git a/lib/libc/stdio/mktemp.c b/lib/libc/stdio/mktemp.c index 8aff226acf14..a2f80ee7b0dd 100644 --- a/lib/libc/stdio/mktemp.c +++ b/lib/libc/stdio/mktemp.c @@ -121,7 +121,7 @@ _gettemp(int dfd, char *path, int *doopen, int domkdir, int slen, int oflags) if ((doopen != NULL && domkdir) || slen < 0 || (oflags & ~(O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC | - O_CLOEXEC)) != 0) { + O_CLOEXEC | O_CLOFORK)) != 0) { errno = EINVAL; return (0); } diff --git a/lib/libc/stdio/vfscanf.c b/lib/libc/stdio/vfscanf.c index a678710e1ecb..89e9e843969f 100644 --- a/lib/libc/stdio/vfscanf.c +++ b/lib/libc/stdio/vfscanf.c @@ -318,10 +318,9 @@ parseint_fsm(int c, enum parseint_state *state, int *base) case '0': if (*state == begin || *state == havesign) { *state = havezero; - } else { - *state = any; + return 1; } - return 1; + /* FALL THROUGH */ case '1': case '2': case '3': diff --git a/lib/libc/stdio/vfwscanf.c b/lib/libc/stdio/vfwscanf.c index 57206a8407d5..7ca64eb37811 100644 --- a/lib/libc/stdio/vfwscanf.c +++ b/lib/libc/stdio/vfwscanf.c @@ -298,10 +298,9 @@ parseint_fsm(wchar_t c, enum parseint_state *state, int *base) case '0': if (*state == begin || *state == havesign) { *state = havezero; - } else { - *state = any; + return 1; } - return 1; + /* FALL THROUGH */ case '1': case '2': case '3': diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index bea82445304d..e7b9955b9646 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -10,7 +10,7 @@ MISRCS+=C99_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \ insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c \ merge.c mergesort_b.c ptsname.c qsort.c qsort_r.c qsort_r_compat.c \ qsort_s.c quick_exit.c radixsort.c rand.c \ - random.c reallocarray.c reallocf.c realpath.c remque.c \ + random.c reallocarray.c reallocf.c realpath.c recallocarray.c remque.c \ set_constraint_handler_s.c strfmon.c strtoimax.c \ strtol.c strtold.c strtoll.c strtoq.c strtoul.c strtonum.c strtoull.c \ strtoumax.c strtouq.c system.c tdelete.c tfind.c tsearch.c twalk.c @@ -31,7 +31,7 @@ SYM_MAPS+= ${LIBC_SRCTOP}/stdlib/Symbol.map # machine-dependent stdlib sources .sinclude "${LIBC_SRCTOP}/${LIBC_ARCH}/stdlib/Makefile.inc" -MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 \ +MAN+= a64l.3 abort.3 abs.3 atexit.3 atof.3 \ atoi.3 atol.3 at_quick_exit.3 bsearch.3 \ div.3 exit.3 getenv.3 getopt.3 getopt_long.3 getsubopt.3 \ hcreate.3 imaxabs.3 imaxdiv.3 insque.3 labs.3 ldiv.3 llabs.3 lldiv.3 \ @@ -76,6 +76,7 @@ MLINKS+=random.3 initstate.3 \ random.3 srandom.3 \ random.3 srandomdev.3 MLINKS+=radixsort.3 sradixsort.3 +MLINKS+=reallocarray.3 recallocarray.3 MLINKS+=set_constraint_handler_s.3 abort_handler_s.3 MLINKS+=set_constraint_handler_s.3 ignore_handler_s.3 MLINKS+=strfmon.3 strfmon_l.3 diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map index d74df869cf54..53d71bcafb7d 100644 --- a/lib/libc/stdlib/Symbol.map +++ b/lib/libc/stdlib/Symbol.map @@ -127,6 +127,14 @@ FBSD_1.7 { secure_getenv; }; +FBSD_1.8 { + getenv_r; +}; + +FBSD_1.9 { + recallocarray; +}; + FBSDprivate_1.0 { __system; _system; diff --git a/lib/libc/stdlib/alloca.3 b/lib/libc/stdlib/alloca.3 deleted file mode 100644 index d2111872ae0b..000000000000 --- a/lib/libc/stdlib/alloca.3 +++ /dev/null @@ -1,85 +0,0 @@ -.\" Copyright (c) 1980, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.Dd September 5, 2006 -.Dt ALLOCA 3 -.Os -.Sh NAME -.Nm alloca -.Nd memory allocator -.Sh LIBRARY -.Lb libc -.Sh SYNOPSIS -.In stdlib.h -.Ft void * -.Fn alloca "size_t size" -.Sh DESCRIPTION -The -.Fn alloca -function -allocates -.Fa size -bytes of space in the stack frame of the caller. -This temporary space is automatically freed on -return. -.Sh RETURN VALUES -The -.Fn alloca -function returns a pointer to the beginning of the allocated space. -.Sh SEE ALSO -.Xr brk 2 , -.Xr calloc 3 , -.Xr getpagesize 3 , -.Xr malloc 3 , -.Xr realloc 3 -.Sh HISTORY -The -.Fn alloca -function appeared in -.At 32v . -.\" .Bx ?? . -.\" The function appeared in 32v, pwb and pwb.2 and in 3bsd 4bsd -.\" The first man page (or link to a man page that I can find at the -.\" moment is 4.3... -.Sh BUGS -The -.Fn alloca -function -is machine and compiler dependent; -its use is discouraged. -.Pp -The -.Fn alloca -function is slightly unsafe because it cannot ensure that the pointer -returned points to a valid and usable block of memory. -The allocation made may exceed the bounds of the stack, or even go -further into other objects in memory, and -.Fn alloca -cannot determine such an error. -Avoid -.Fn alloca -with large unbounded allocations. diff --git a/lib/libc/stdlib/atexit.c b/lib/libc/stdlib/atexit.c index e5aa66c51f38..6e4a12f9e530 100644 --- a/lib/libc/stdlib/atexit.c +++ b/lib/libc/stdlib/atexit.c @@ -35,6 +35,7 @@ #include "namespace.h" #include <errno.h> #include <link.h> +#include <stdbool.h> #include <stddef.h> #include <stdlib.h> #include <unistd.h> @@ -56,6 +57,8 @@ _Block_copy(void*); #define ATEXIT_FN_CXA 2 static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER; +static void *current_finalize_dso = NULL; +static bool call_finalize_again = false; #define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x) #define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x) @@ -115,6 +118,9 @@ atexit_register(struct atexit_fn *fptr) __atexit = p; } p->fns[p->ind++] = *fptr; + if (current_finalize_dso != NULL && + current_finalize_dso == fptr->fn_dso) + call_finalize_again = true; _MUTEX_UNLOCK(&atexit_mutex); return 0; } @@ -208,33 +214,38 @@ __cxa_finalize(void *dso) } _MUTEX_LOCK(&atexit_mutex); - for (p = __atexit; p; p = p->next) { - for (n = p->ind; --n >= 0;) { - if (p->fns[n].fn_type == ATEXIT_FN_EMPTY) - continue; /* already been called */ - fn = p->fns[n]; - if (dso != NULL && dso != fn.fn_dso) { - /* wrong DSO ? */ - if (!has_phdr || global_exit || - !__elf_phdr_match_addr(&phdr_info, - fn.fn_ptr.cxa_func)) - continue; + current_finalize_dso = dso; + do { + call_finalize_again = false; + for (p = __atexit; p; p = p->next) { + for (n = p->ind; --n >= 0;) { + if (p->fns[n].fn_type == ATEXIT_FN_EMPTY) + continue; /* already been called */ + fn = p->fns[n]; + if (dso != NULL && dso != fn.fn_dso) { + /* wrong DSO ? */ + if (!has_phdr || global_exit || + !__elf_phdr_match_addr(&phdr_info, + fn.fn_ptr.cxa_func)) + continue; + } + /* + Mark entry to indicate that this particular + handler has already been called. + */ + p->fns[n].fn_type = ATEXIT_FN_EMPTY; + _MUTEX_UNLOCK(&atexit_mutex); + + /* Call the function of correct type. */ + if (fn.fn_type == ATEXIT_FN_CXA) + fn.fn_ptr.cxa_func(fn.fn_arg); + else if (fn.fn_type == ATEXIT_FN_STD) + fn.fn_ptr.std_func(); + _MUTEX_LOCK(&atexit_mutex); } - /* - Mark entry to indicate that this particular handler - has already been called. - */ - p->fns[n].fn_type = ATEXIT_FN_EMPTY; - _MUTEX_UNLOCK(&atexit_mutex); - - /* Call the function of correct type. */ - if (fn.fn_type == ATEXIT_FN_CXA) - fn.fn_ptr.cxa_func(fn.fn_arg); - else if (fn.fn_type == ATEXIT_FN_STD) - fn.fn_ptr.std_func(); - _MUTEX_LOCK(&atexit_mutex); } - } + } while (call_finalize_again); + current_finalize_dso = NULL; _MUTEX_UNLOCK(&atexit_mutex); if (dso == NULL) _MUTEX_DESTROY(&atexit_mutex); diff --git a/lib/libc/stdlib/getenv.3 b/lib/libc/stdlib/getenv.3 index 1337ec867237..045a1831dfdc 100644 --- a/lib/libc/stdlib/getenv.3 +++ b/lib/libc/stdlib/getenv.3 @@ -29,12 +29,13 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 14, 2023 +.Dd April 22, 2025 .Dt GETENV 3 .Os .Sh NAME .Nm clearenv , .Nm getenv , +.Nm getenv_r , .Nm putenv , .Nm secure_getenv , .Nm setenv , @@ -48,6 +49,8 @@ .Fn clearenv "void" .Ft char * .Fn getenv "const char *name" +.Ft int +.Fn getenv_r "const char *name" "char *buf" "size_t len" .Ft char * .Fn secure_getenv "const char *name" .Ft int @@ -71,7 +74,8 @@ and .Pp The .Fn getenv -function obtains the current value of the environment variable, +function obtains the current value of the environment variable +designated by .Fa name . The application should not modify the string pointed to by the @@ -79,13 +83,23 @@ to by the function. .Pp The +.Fn getenv_r +function obtains the current value of the environment variable +designated by +.Fa name +and copies it into the buffer +.Fa buf +of length +.Fa len . +.Pp +The .Fn secure_getenv returns .Va NULL when the environment cannot be trusted, otherwise it acts like .Fn getenv . The environment currently is not trusted when -.Xr issetugid 3 +.Xr issetugid 2 returns a non-zero value, but other conditions may be added in the future. .Pp @@ -157,12 +171,13 @@ function returns if the process is in "secure execution," otherwise it will call .Fn getenv . .Pp -.Rv -std clearenv setenv putenv unsetenv +.Rv -std clearenv getenv_r setenv putenv unsetenv .Sh ERRORS .Bl -tag -width Er .It Bq Er EINVAL The function .Fn getenv , +.Fn getenv_r , .Fn setenv or .Fn unsetenv @@ -191,6 +206,11 @@ is the first character in This does not follow the .Tn POSIX specification. +.It Bq Er ENOENT +The function +.Fn getenv_r +failed because the requested variable was not found in the +environment. .It Bq Er ENOMEM The function .Fn setenv , @@ -198,6 +218,11 @@ The function or .Fn putenv failed because they were unable to allocate memory for the environment. +.It Bq Er ERANGE +The function +.Fn getenv_r +failed because the value of the requested variable was too long to fit +in the provided buffer. .El .Sh SEE ALSO .Xr csh 1 , @@ -251,6 +276,13 @@ and .Fn secure_getenv functions were added in .Fx 14 . +.Pp +The +.Fn getenv_r +function first appeared in +.Nx 4.0 +and was added in +.Fx 15 . .Sh BUGS Successive calls to .Fn setenv diff --git a/lib/libc/stdlib/getenv.c b/lib/libc/stdlib/getenv.c index f8f59526421a..c1d0b7a559d5 100644 --- a/lib/libc/stdlib/getenv.c +++ b/lib/libc/stdlib/getenv.c @@ -28,6 +28,7 @@ #include "namespace.h" #include <sys/types.h> +#include <ssp/ssp.h> #include <errno.h> #include <stdbool.h> #include <stddef.h> @@ -444,6 +445,41 @@ getenv(const char *name) /* + * Like getenv(), but copies the value into the provided buffer. + */ +int +__ssp_real(getenv_r)(const char *name, char *buf, size_t len) +{ + const char *val; + size_t nameLen; + int envNdx; + + if (name == NULL || (nameLen = __strleneq(name)) == 0) { + errno = EINVAL; + return (-1); + } + + if (environ == NULL || environ[0] == NULL) { + val = NULL; + } else if (envVars == NULL || environ != intEnviron) { + val = __findenv_environ(name, nameLen); + } else { + envNdx = envVarsTotal - 1; + val = __findenv(name, nameLen, &envNdx, true); + } + if (val == NULL) { + errno = ENOENT; + return (-1); + } + if (strlcpy(buf, val, len) >= len) { + errno = ERANGE; + return (-1); + } + return (0); +} + + +/* * Runs getenv() unless the current process is tainted by uid or gid changes, in * which case it will return NULL. */ diff --git a/lib/libc/stdlib/getopt_long.3 b/lib/libc/stdlib/getopt_long.3 index f8bb1f1b6b11..d6998d13822a 100644 --- a/lib/libc/stdlib/getopt_long.3 +++ b/lib/libc/stdlib/getopt_long.3 @@ -217,7 +217,7 @@ an error and return Specifying .Ql \&: in -.Fa optstr +.Fa optstring will cause the error message to be suppressed and .Ql \&: to be returned instead. @@ -229,7 +229,7 @@ a leading or .Ql \&- in -.Fa optstr +.Fa optstring also has special meaning. If either of these are specified, they must appear before .Ql \&: . diff --git a/lib/libc/stdlib/malloc/jemalloc/Makefile.inc b/lib/libc/stdlib/malloc/jemalloc/Makefile.inc index c10d79dbce6c..7722a7b755f3 100644 --- a/lib/libc/stdlib/malloc/jemalloc/Makefile.inc +++ b/lib/libc/stdlib/malloc/jemalloc/Makefile.inc @@ -1,15 +1,21 @@ .PATH: ${LIBC_SRCTOP}/stdlib/malloc/jemalloc JEMALLOCSRCS:= jemalloc.c arena.c background_thread.c base.c bin.c bitmap.c \ - ckh.c ctl.c div.c extent.c extent_dss.c extent_mmap.c hash.c hook.c \ - large.c log.c malloc_io.c mutex.c mutex_pool.c nstime.c pages.c \ - prng.c prof.c rtree.c safety_check.c sc.c stats.c sz.c tcache.c \ - test_hooks.c ticker.c tsd.c witness.c + ckh.c ctl.c div.c extent.c extent_dss.c extent_mmap.c hook.c \ + large.c log.c malloc_io.c mutex.c nstime.c pages.c \ + prof.c rtree.c safety_check.c sc.c stats.c sz.c tcache.c \ + test_hooks.c ticker.c tsd.c witness.c \ + bin_info.c san.c san_bump.c counter.c prof_data.c prof_log.c prof_recent.c prof_stats.c prof_sys.c \ + emap.c edata.c edata_cache.c pa.c pa_extra.c pac.c decay.c hpa.c hpa_hooks.c fxp.c hpdata.c pai.c \ + ecache.c ehooks.c eset.c sec.c cache_bin.c peak_event.c psset.c inspect.c exp_grow.c thread_event.c \ + buf_writer.c CFLAGS+=-I${SRCTOP}/contrib/jemalloc/include -I${LIBC_SRCTOP}/stdlib/malloc/jemalloc/include .if ${MK_JEMALLOC_LG_VADDR_WIDE} != no CFLAGS+=-D_USE_LG_VADDR_WIDE .endif +CFLAGS.gcc+=-Wno-error=missing-braces + .for src in ${JEMALLOCSRCS} MISRCS+=jemalloc_${src} diff --git a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h index 1aedb916976b..900ae867f321 100644 --- a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h +++ b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h @@ -45,17 +45,17 @@ #define LG_VADDR 48 /* Defined if C11 atomics are available. */ -#define JEMALLOC_C11_ATOMICS 1 +#define JEMALLOC_C11_ATOMICS /* Defined if GCC __atomic atomics are available. */ -#define JEMALLOC_GCC_ATOMIC_ATOMICS 1 +#define JEMALLOC_GCC_ATOMIC_ATOMICS /* and the 8-bit variant support. */ -#define JEMALLOC_GCC_U8_ATOMIC_ATOMICS 1 +#define JEMALLOC_GCC_U8_ATOMIC_ATOMICS /* Defined if GCC __sync atomics are available. */ -#define JEMALLOC_GCC_SYNC_ATOMICS 1 +#define JEMALLOC_GCC_SYNC_ATOMICS /* and the 8-bit variant support. */ -#define JEMALLOC_GCC_U8_SYNC_ATOMICS 1 +#define JEMALLOC_GCC_U8_SYNC_ATOMICS /* * Defined if __builtin_clz() and __builtin_clzl() are available. @@ -73,7 +73,7 @@ /* * Defined if secure_getenv(3) is available. */ -/* #undef JEMALLOC_HAVE_SECURE_GETENV */ +#define JEMALLOC_HAVE_SECURE_GETENV /* * Defined if issetugid(2) is available. @@ -84,17 +84,23 @@ #define JEMALLOC_HAVE_PTHREAD_ATFORK /* Defined if pthread_setname_np(3) is available. */ -/* #undef JEMALLOC_HAVE_PTHREAD_SETNAME_NP */ +#define JEMALLOC_HAVE_PTHREAD_SETNAME_NP + +/* Defined if pthread_getname_np(3) is available. */ +#define JEMALLOC_HAVE_PTHREAD_GETNAME_NP + +/* Defined if pthread_get_name_np(3) is available. */ +#define JEMALLOC_HAVE_PTHREAD_GET_NAME_NP /* * Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available. */ -/* #undef JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE */ +#define JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE /* * Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available. */ -#define JEMALLOC_HAVE_CLOCK_MONOTONIC 1 +#define JEMALLOC_HAVE_CLOCK_MONOTONIC /* * Defined if mach_absolute_time() is available. @@ -102,6 +108,11 @@ /* #undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME */ /* + * Defined if clock_gettime(CLOCK_REALTIME, ...) is available. + */ +#define JEMALLOC_HAVE_CLOCK_REALTIME + +/* * Defined if _malloc_thread_cleanup() exists. At least in the case of * FreeBSD, pthread_key_create() allocates, which if used during malloc * bootstrapping will cause recursion into the pthreads library. Therefore, if @@ -122,7 +133,7 @@ * _pthread_mutex_init_calloc_cb(), in which case the function is used in order * to avoid recursive allocation during mutex initialization. */ -#define JEMALLOC_MUTEX_INIT_CB 1 +#define JEMALLOC_MUTEX_INIT_CB /* Non-empty if the tls_model attribute is supported. */ #define JEMALLOC_TLS_MODEL __attribute__((tls_model("initial-exec"))) @@ -163,6 +174,9 @@ /* Support utrace(2)-based tracing. */ #define JEMALLOC_UTRACE +/* Support utrace(2)-based tracing (label based signature). */ +/* #undef JEMALLOC_UTRACE_LABEL */ + /* Support optional abort() on OOM. */ #define JEMALLOC_XMALLOC @@ -178,6 +192,9 @@ /* One page is 2^LG_PAGE bytes. */ #define LG_PAGE 12 +/* Maximum number of regions in a slab. */ +/* #undef CONFIG_LG_SLAB_MAXREGS */ + /* * One huge page is 2^LG_HUGEPAGE bytes. Note that this is defined even if the * system does not explicitly support huge pages; system calls that require @@ -292,16 +309,45 @@ /* #undef JEMALLOC_MADVISE_DONTDUMP */ /* + * Defined if MADV_[NO]CORE is supported as an argument to madvise. + */ +#define JEMALLOC_MADVISE_NOCORE + +/* Defined if mprotect(2) is available. */ +#define JEMALLOC_HAVE_MPROTECT + +/* * Defined if transparent huge pages (THPs) are supported via the * MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled. */ /* #undef JEMALLOC_THP */ +/* Defined if posix_madvise is available. */ +/* #undef JEMALLOC_HAVE_POSIX_MADVISE */ + +/* + * Method for purging unused pages using posix_madvise. + * + * posix_madvise(..., POSIX_MADV_DONTNEED) + */ +/* #undef JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED */ +/* #undef JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED_ZEROS */ + +/* + * Defined if memcntl page admin call is supported + */ +/* #undef JEMALLOC_HAVE_MEMCNTL */ + +/* + * Defined if malloc_size is supported + */ +/* #undef JEMALLOC_HAVE_MALLOC_SIZE */ + /* Define if operating system has alloca.h header. */ /* #undef JEMALLOC_HAS_ALLOCA_H */ /* C99 restrict keyword supported. */ -#define JEMALLOC_HAS_RESTRICT 1 +#define JEMALLOC_HAS_RESTRICT /* For use by hash code. */ /* #undef JEMALLOC_BIG_ENDIAN */ @@ -334,15 +380,15 @@ #define JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP /* GNU specific sched_getcpu support */ -/* #undef JEMALLOC_HAVE_SCHED_GETCPU */ +#define JEMALLOC_HAVE_SCHED_GETCPU /* GNU specific sched_setaffinity support */ -/* #undef JEMALLOC_HAVE_SCHED_SETAFFINITY */ +#define JEMALLOC_HAVE_SCHED_SETAFFINITY /* * If defined, all the features necessary for background threads are present. */ -#define JEMALLOC_BACKGROUND_THREAD 1 +#define JEMALLOC_BACKGROUND_THREAD /* * If defined, jemalloc symbols are not exported (doesn't work when @@ -354,7 +400,7 @@ #define JEMALLOC_CONFIG_MALLOC_CONF "abort_conf:false" /* If defined, jemalloc takes the malloc/free/etc. symbol names. */ -#define JEMALLOC_IS_MALLOC 1 +#define JEMALLOC_IS_MALLOC /* * Defined if strerror_r returns char * if _GNU_SOURCE is defined. @@ -364,4 +410,19 @@ /* Performs additional safety checks when defined. */ /* #undef JEMALLOC_OPT_SAFETY_CHECKS */ +/* Is C++ support being built? */ +#define JEMALLOC_ENABLE_CXX + +/* Performs additional size checks when defined. */ +/* #undef JEMALLOC_OPT_SIZE_CHECKS */ + +/* Allows sampled junk and stash for checking use-after-free when defined. */ +/* #undef JEMALLOC_UAF_DETECTION */ + +/* Darwin VM_MAKE_TAG support */ +/* #undef JEMALLOC_HAVE_VM_MAKE_TAG */ + +/* If defined, realloc(ptr, 0) defaults to "free" instead of "alloc". */ +/* #undef JEMALLOC_ZERO_REALLOC_DEFAULT_FREE */ + #endif /* JEMALLOC_INTERNAL_DEFS_H_ */ diff --git a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_preamble.h b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_preamble.h index d1dcc50d5a3b..b6cf5864109d 100644 --- a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_preamble.h +++ b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/jemalloc_preamble.h @@ -4,12 +4,20 @@ #include "jemalloc_internal_defs.h" #include "jemalloc/internal/jemalloc_internal_decls.h" -#ifdef JEMALLOC_UTRACE +#if defined(JEMALLOC_UTRACE) || defined(JEMALLOC_UTRACE_LABEL) #include <sys/ktrace.h> +# if defined(JEMALLOC_UTRACE) +# define UTRACE_CALL(p, l) utrace(p, l) +# else +# define UTRACE_CALL(p, l) utrace("jemalloc_process", p, l) +# define JEMALLOC_UTRACE +# endif #endif +#ifndef JEMALLOC_PRIVATE_NAMESPACE #include "un-namespace.h" #include "libc_private.h" +#endif #define JEMALLOC_NO_DEMANGLE #ifdef JEMALLOC_JET @@ -177,6 +185,35 @@ static const bool config_opt_safety_checks = #endif ; +/* + * Extra debugging of sized deallocations too onerous to be included in the + * general safety checks. + */ +static const bool config_opt_size_checks = +#if defined(JEMALLOC_OPT_SIZE_CHECKS) || defined(JEMALLOC_DEBUG) + true +#else + false +#endif + ; + +static const bool config_uaf_detection = +#if defined(JEMALLOC_UAF_DETECTION) || defined(JEMALLOC_DEBUG) + true +#else + false +#endif + ; + +/* Whether or not the C++ extensions are enabled. */ +static const bool config_enable_cxx = +#ifdef JEMALLOC_ENABLE_CXX + true +#else + false +#endif +; + #if defined(_WIN32) || defined(JEMALLOC_HAVE_SCHED_GETCPU) /* Currently percpu_arena depends on sched_getcpu. */ #define JEMALLOC_PERCPU_ARENA @@ -206,5 +243,20 @@ static const bool have_background_thread = false #endif ; +static const bool config_high_res_timer = +#ifdef JEMALLOC_HAVE_CLOCK_REALTIME + true +#else + false +#endif + ; + +static const bool have_memcntl = +#ifdef JEMALLOC_HAVE_MEMCNTL + true +#else + false +#endif + ; #endif /* JEMALLOC_PREAMBLE_H */ diff --git a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/private_namespace.h b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/private_namespace.h index a448ad63fa54..08ce9219cbf8 100644 --- a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/private_namespace.h +++ b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/private_namespace.h @@ -5,10 +5,9 @@ #define arena_init JEMALLOC_N(arena_init) #define arena_migrate JEMALLOC_N(arena_migrate) #define arena_set JEMALLOC_N(arena_set) -#define arena_tdata_get_hard JEMALLOC_N(arena_tdata_get_hard) #define arenas JEMALLOC_N(arenas) #define arenas_lock JEMALLOC_N(arenas_lock) -#define arenas_tdata_cleanup JEMALLOC_N(arenas_tdata_cleanup) +#define batch_alloc JEMALLOC_N(batch_alloc) #define bootstrap_calloc JEMALLOC_N(bootstrap_calloc) #define bootstrap_free JEMALLOC_N(bootstrap_free) #define bootstrap_malloc JEMALLOC_N(bootstrap_malloc) @@ -16,8 +15,10 @@ #define iarena_cleanup JEMALLOC_N(iarena_cleanup) #define je_sdallocx_noflags JEMALLOC_N(je_sdallocx_noflags) #define jemalloc_postfork_child JEMALLOC_N(jemalloc_postfork_child) +#define junk_alloc_callback JEMALLOC_N(junk_alloc_callback) +#define junk_free_callback JEMALLOC_N(junk_free_callback) #define malloc_default JEMALLOC_N(malloc_default) -#define malloc_initialized JEMALLOC_N(malloc_initialized) +#define malloc_init_state JEMALLOC_N(malloc_init_state) #define malloc_slow JEMALLOC_N(malloc_slow) #define manual_arena_base JEMALLOC_N(manual_arena_base) #define narenas_auto JEMALLOC_N(narenas_auto) @@ -25,49 +26,64 @@ #define ncpus JEMALLOC_N(ncpus) #define opt_abort JEMALLOC_N(opt_abort) #define opt_abort_conf JEMALLOC_N(opt_abort_conf) +#define opt_cache_oblivious JEMALLOC_N(opt_cache_oblivious) #define opt_confirm_conf JEMALLOC_N(opt_confirm_conf) +#define opt_experimental_infallible_new JEMALLOC_N(opt_experimental_infallible_new) +#define opt_hpa JEMALLOC_N(opt_hpa) +#define opt_hpa_opts JEMALLOC_N(opt_hpa_opts) +#define opt_hpa_sec_opts JEMALLOC_N(opt_hpa_sec_opts) #define opt_junk JEMALLOC_N(opt_junk) #define opt_junk_alloc JEMALLOC_N(opt_junk_alloc) #define opt_junk_free JEMALLOC_N(opt_junk_free) #define opt_narenas JEMALLOC_N(opt_narenas) +#define opt_narenas_ratio JEMALLOC_N(opt_narenas_ratio) +#define opt_trust_madvise JEMALLOC_N(opt_trust_madvise) #define opt_utrace JEMALLOC_N(opt_utrace) #define opt_xmalloc JEMALLOC_N(opt_xmalloc) #define opt_zero JEMALLOC_N(opt_zero) +#define opt_zero_realloc_action JEMALLOC_N(opt_zero_realloc_action) #define sdallocx_default JEMALLOC_N(sdallocx_default) -#define arena_alloc_junk_small JEMALLOC_N(arena_alloc_junk_small) +#define zero_realloc_count JEMALLOC_N(zero_realloc_count) +#define zero_realloc_mode_names JEMALLOC_N(zero_realloc_mode_names) #define arena_basic_stats_merge JEMALLOC_N(arena_basic_stats_merge) -#define arena_bin_choose_lock JEMALLOC_N(arena_bin_choose_lock) +#define arena_bin_choose JEMALLOC_N(arena_bin_choose) +#define arena_bin_offsets JEMALLOC_N(arena_bin_offsets) +#define arena_binind_div_info JEMALLOC_N(arena_binind_div_info) #define arena_boot JEMALLOC_N(arena_boot) +#define arena_cache_bin_fill_small JEMALLOC_N(arena_cache_bin_fill_small) #define arena_choose_huge JEMALLOC_N(arena_choose_huge) -#define arena_dalloc_bin_junked_locked JEMALLOC_N(arena_dalloc_bin_junked_locked) -#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small) +#define arena_config_default JEMALLOC_N(arena_config_default) +#define arena_dalloc_bin_locked_handle_newly_empty JEMALLOC_N(arena_dalloc_bin_locked_handle_newly_empty) +#define arena_dalloc_bin_locked_handle_newly_nonempty JEMALLOC_N(arena_dalloc_bin_locked_handle_newly_nonempty) #define arena_dalloc_promoted JEMALLOC_N(arena_dalloc_promoted) #define arena_dalloc_small JEMALLOC_N(arena_dalloc_small) #define arena_decay JEMALLOC_N(arena_decay) +#define arena_decay_ms_get JEMALLOC_N(arena_decay_ms_get) +#define arena_decay_ms_set JEMALLOC_N(arena_decay_ms_set) #define arena_destroy JEMALLOC_N(arena_destroy) #define arena_dirty_decay_ms_default_get JEMALLOC_N(arena_dirty_decay_ms_default_get) #define arena_dirty_decay_ms_default_set JEMALLOC_N(arena_dirty_decay_ms_default_set) -#define arena_dirty_decay_ms_get JEMALLOC_N(arena_dirty_decay_ms_get) -#define arena_dirty_decay_ms_set JEMALLOC_N(arena_dirty_decay_ms_set) +#define arena_do_deferred_work JEMALLOC_N(arena_do_deferred_work) #define arena_dss_prec_get JEMALLOC_N(arena_dss_prec_get) #define arena_dss_prec_set JEMALLOC_N(arena_dss_prec_set) +#define arena_emap_global JEMALLOC_N(arena_emap_global) #define arena_extent_alloc_large JEMALLOC_N(arena_extent_alloc_large) #define arena_extent_dalloc_large_prep JEMALLOC_N(arena_extent_dalloc_large_prep) #define arena_extent_ralloc_large_expand JEMALLOC_N(arena_extent_ralloc_large_expand) #define arena_extent_ralloc_large_shrink JEMALLOC_N(arena_extent_ralloc_large_shrink) -#define arena_extent_sn_next JEMALLOC_N(arena_extent_sn_next) -#define arena_extents_dirty_dalloc JEMALLOC_N(arena_extents_dirty_dalloc) +#define arena_fill_small_fresh JEMALLOC_N(arena_fill_small_fresh) +#define arena_get_ehooks JEMALLOC_N(arena_get_ehooks) +#define arena_handle_deferred_work JEMALLOC_N(arena_handle_deferred_work) #define arena_init_huge JEMALLOC_N(arena_init_huge) #define arena_is_huge JEMALLOC_N(arena_is_huge) #define arena_malloc_hard JEMALLOC_N(arena_malloc_hard) #define arena_muzzy_decay_ms_default_get JEMALLOC_N(arena_muzzy_decay_ms_default_get) #define arena_muzzy_decay_ms_default_set JEMALLOC_N(arena_muzzy_decay_ms_default_set) -#define arena_muzzy_decay_ms_get JEMALLOC_N(arena_muzzy_decay_ms_get) -#define arena_muzzy_decay_ms_set JEMALLOC_N(arena_muzzy_decay_ms_set) #define arena_new JEMALLOC_N(arena_new) #define arena_nthreads_dec JEMALLOC_N(arena_nthreads_dec) #define arena_nthreads_get JEMALLOC_N(arena_nthreads_get) #define arena_nthreads_inc JEMALLOC_N(arena_nthreads_inc) +#define arena_pa_central_global JEMALLOC_N(arena_pa_central_global) #define arena_palloc JEMALLOC_N(arena_palloc) #define arena_postfork_child JEMALLOC_N(arena_postfork_child) #define arena_postfork_parent JEMALLOC_N(arena_postfork_parent) @@ -79,14 +95,15 @@ #define arena_prefork5 JEMALLOC_N(arena_prefork5) #define arena_prefork6 JEMALLOC_N(arena_prefork6) #define arena_prefork7 JEMALLOC_N(arena_prefork7) +#define arena_prefork8 JEMALLOC_N(arena_prefork8) #define arena_prof_promote JEMALLOC_N(arena_prof_promote) #define arena_ralloc JEMALLOC_N(arena_ralloc) #define arena_ralloc_no_move JEMALLOC_N(arena_ralloc_no_move) #define arena_reset JEMALLOC_N(arena_reset) #define arena_retain_grow_limit_get_set JEMALLOC_N(arena_retain_grow_limit_get_set) +#define arena_set_extent_hooks JEMALLOC_N(arena_set_extent_hooks) +#define arena_slab_dalloc JEMALLOC_N(arena_slab_dalloc) #define arena_stats_merge JEMALLOC_N(arena_stats_merge) -#define arena_tcache_fill_small JEMALLOC_N(arena_tcache_fill_small) -#define h_steps JEMALLOC_N(h_steps) #define opt_dirty_decay_ms JEMALLOC_N(opt_dirty_decay_ms) #define opt_muzzy_decay_ms JEMALLOC_N(opt_muzzy_decay_ms) #define opt_oversize_threshold JEMALLOC_N(opt_oversize_threshold) @@ -99,13 +116,14 @@ #define background_thread_ctl_init JEMALLOC_N(background_thread_ctl_init) #define background_thread_enabled_state JEMALLOC_N(background_thread_enabled_state) #define background_thread_info JEMALLOC_N(background_thread_info) -#define background_thread_interval_check JEMALLOC_N(background_thread_interval_check) +#define background_thread_is_started JEMALLOC_N(background_thread_is_started) #define background_thread_lock JEMALLOC_N(background_thread_lock) #define background_thread_postfork_child JEMALLOC_N(background_thread_postfork_child) #define background_thread_postfork_parent JEMALLOC_N(background_thread_postfork_parent) #define background_thread_prefork0 JEMALLOC_N(background_thread_prefork0) #define background_thread_prefork1 JEMALLOC_N(background_thread_prefork1) #define background_thread_stats_read JEMALLOC_N(background_thread_stats_read) +#define background_thread_wakeup_early JEMALLOC_N(background_thread_wakeup_early) #define background_threads_disable JEMALLOC_N(background_threads_disable) #define background_threads_enable JEMALLOC_N(background_threads_enable) #define max_background_threads JEMALLOC_N(max_background_threads) @@ -115,10 +133,11 @@ #define pthread_create_wrapper JEMALLOC_N(pthread_create_wrapper) #define b0get JEMALLOC_N(b0get) #define base_alloc JEMALLOC_N(base_alloc) -#define base_alloc_extent JEMALLOC_N(base_alloc_extent) +#define base_alloc_edata JEMALLOC_N(base_alloc_edata) #define base_boot JEMALLOC_N(base_boot) #define base_delete JEMALLOC_N(base_delete) -#define base_extent_hooks_get JEMALLOC_N(base_extent_hooks_get) +#define base_ehooks_get JEMALLOC_N(base_ehooks_get) +#define base_ehooks_get_for_metadata JEMALLOC_N(base_ehooks_get_for_metadata) #define base_extent_hooks_set JEMALLOC_N(base_extent_hooks_set) #define base_new JEMALLOC_N(base_new) #define base_postfork_child JEMALLOC_N(base_postfork_child) @@ -127,17 +146,28 @@ #define base_stats_get JEMALLOC_N(base_stats_get) #define metadata_thp_mode_names JEMALLOC_N(metadata_thp_mode_names) #define opt_metadata_thp JEMALLOC_N(opt_metadata_thp) -#define bin_boot JEMALLOC_N(bin_boot) -#define bin_infos JEMALLOC_N(bin_infos) #define bin_init JEMALLOC_N(bin_init) #define bin_postfork_child JEMALLOC_N(bin_postfork_child) #define bin_postfork_parent JEMALLOC_N(bin_postfork_parent) #define bin_prefork JEMALLOC_N(bin_prefork) #define bin_shard_sizes_boot JEMALLOC_N(bin_shard_sizes_boot) #define bin_update_shard_size JEMALLOC_N(bin_update_shard_size) +#define bin_info_boot JEMALLOC_N(bin_info_boot) +#define bin_infos JEMALLOC_N(bin_infos) #define bitmap_info_init JEMALLOC_N(bitmap_info_init) #define bitmap_init JEMALLOC_N(bitmap_init) #define bitmap_size JEMALLOC_N(bitmap_size) +#define buf_writer_cb JEMALLOC_N(buf_writer_cb) +#define buf_writer_flush JEMALLOC_N(buf_writer_flush) +#define buf_writer_init JEMALLOC_N(buf_writer_init) +#define buf_writer_pipe JEMALLOC_N(buf_writer_pipe) +#define buf_writer_terminate JEMALLOC_N(buf_writer_terminate) +#define cache_bin_info_compute_alloc JEMALLOC_N(cache_bin_info_compute_alloc) +#define cache_bin_info_init JEMALLOC_N(cache_bin_info_init) +#define cache_bin_init JEMALLOC_N(cache_bin_init) +#define cache_bin_postincrement JEMALLOC_N(cache_bin_postincrement) +#define cache_bin_preincrement JEMALLOC_N(cache_bin_preincrement) +#define cache_bin_still_zero_initialized JEMALLOC_N(cache_bin_still_zero_initialized) #define ckh_count JEMALLOC_N(ckh_count) #define ckh_delete JEMALLOC_N(ckh_delete) #define ckh_insert JEMALLOC_N(ckh_insert) @@ -149,61 +179,116 @@ #define ckh_search JEMALLOC_N(ckh_search) #define ckh_string_hash JEMALLOC_N(ckh_string_hash) #define ckh_string_keycomp JEMALLOC_N(ckh_string_keycomp) +#define counter_accum_init JEMALLOC_N(counter_accum_init) +#define counter_postfork_child JEMALLOC_N(counter_postfork_child) +#define counter_postfork_parent JEMALLOC_N(counter_postfork_parent) +#define counter_prefork JEMALLOC_N(counter_prefork) #define ctl_boot JEMALLOC_N(ctl_boot) #define ctl_bymib JEMALLOC_N(ctl_bymib) +#define ctl_bymibname JEMALLOC_N(ctl_bymibname) #define ctl_byname JEMALLOC_N(ctl_byname) +#define ctl_mibnametomib JEMALLOC_N(ctl_mibnametomib) +#define ctl_mtx_assert_held JEMALLOC_N(ctl_mtx_assert_held) #define ctl_nametomib JEMALLOC_N(ctl_nametomib) #define ctl_postfork_child JEMALLOC_N(ctl_postfork_child) #define ctl_postfork_parent JEMALLOC_N(ctl_postfork_parent) #define ctl_prefork JEMALLOC_N(ctl_prefork) +#define decay_deadline_init JEMALLOC_N(decay_deadline_init) +#define decay_init JEMALLOC_N(decay_init) +#define decay_maybe_advance_epoch JEMALLOC_N(decay_maybe_advance_epoch) +#define decay_ms_valid JEMALLOC_N(decay_ms_valid) +#define decay_npages_purge_in JEMALLOC_N(decay_npages_purge_in) +#define decay_ns_until_purge JEMALLOC_N(decay_ns_until_purge) +#define decay_reinit JEMALLOC_N(decay_reinit) #define div_init JEMALLOC_N(div_init) -#define extent_alloc JEMALLOC_N(extent_alloc) +#define ecache_init JEMALLOC_N(ecache_init) +#define ecache_postfork_child JEMALLOC_N(ecache_postfork_child) +#define ecache_postfork_parent JEMALLOC_N(ecache_postfork_parent) +#define ecache_prefork JEMALLOC_N(ecache_prefork) +#define edata_avail_any JEMALLOC_N(edata_avail_any) +#define edata_avail_empty JEMALLOC_N(edata_avail_empty) +#define edata_avail_first JEMALLOC_N(edata_avail_first) +#define edata_avail_insert JEMALLOC_N(edata_avail_insert) +#define edata_avail_new JEMALLOC_N(edata_avail_new) +#define edata_avail_remove JEMALLOC_N(edata_avail_remove) +#define edata_avail_remove_any JEMALLOC_N(edata_avail_remove_any) +#define edata_avail_remove_first JEMALLOC_N(edata_avail_remove_first) +#define edata_heap_any JEMALLOC_N(edata_heap_any) +#define edata_heap_empty JEMALLOC_N(edata_heap_empty) +#define edata_heap_first JEMALLOC_N(edata_heap_first) +#define edata_heap_insert JEMALLOC_N(edata_heap_insert) +#define edata_heap_new JEMALLOC_N(edata_heap_new) +#define edata_heap_remove JEMALLOC_N(edata_heap_remove) +#define edata_heap_remove_any JEMALLOC_N(edata_heap_remove_any) +#define edata_heap_remove_first JEMALLOC_N(edata_heap_remove_first) +#define edata_cache_fast_disable JEMALLOC_N(edata_cache_fast_disable) +#define edata_cache_fast_get JEMALLOC_N(edata_cache_fast_get) +#define edata_cache_fast_init JEMALLOC_N(edata_cache_fast_init) +#define edata_cache_fast_put JEMALLOC_N(edata_cache_fast_put) +#define edata_cache_get JEMALLOC_N(edata_cache_get) +#define edata_cache_init JEMALLOC_N(edata_cache_init) +#define edata_cache_postfork_child JEMALLOC_N(edata_cache_postfork_child) +#define edata_cache_postfork_parent JEMALLOC_N(edata_cache_postfork_parent) +#define edata_cache_prefork JEMALLOC_N(edata_cache_prefork) +#define edata_cache_put JEMALLOC_N(edata_cache_put) +#define ehooks_default_alloc_impl JEMALLOC_N(ehooks_default_alloc_impl) +#define ehooks_default_commit_impl JEMALLOC_N(ehooks_default_commit_impl) +#define ehooks_default_dalloc_impl JEMALLOC_N(ehooks_default_dalloc_impl) +#define ehooks_default_decommit_impl JEMALLOC_N(ehooks_default_decommit_impl) +#define ehooks_default_destroy_impl JEMALLOC_N(ehooks_default_destroy_impl) +#define ehooks_default_extent_hooks JEMALLOC_N(ehooks_default_extent_hooks) +#define ehooks_default_guard_impl JEMALLOC_N(ehooks_default_guard_impl) +#define ehooks_default_merge JEMALLOC_N(ehooks_default_merge) +#define ehooks_default_merge_impl JEMALLOC_N(ehooks_default_merge_impl) +#define ehooks_default_purge_forced_impl JEMALLOC_N(ehooks_default_purge_forced_impl) +#define ehooks_default_purge_lazy_impl JEMALLOC_N(ehooks_default_purge_lazy_impl) +#define ehooks_default_split_impl JEMALLOC_N(ehooks_default_split_impl) +#define ehooks_default_unguard_impl JEMALLOC_N(ehooks_default_unguard_impl) +#define ehooks_default_zero_impl JEMALLOC_N(ehooks_default_zero_impl) +#define ehooks_init JEMALLOC_N(ehooks_init) +#define emap_deregister_boundary JEMALLOC_N(emap_deregister_boundary) +#define emap_deregister_interior JEMALLOC_N(emap_deregister_interior) +#define emap_do_assert_mapped JEMALLOC_N(emap_do_assert_mapped) +#define emap_do_assert_not_mapped JEMALLOC_N(emap_do_assert_not_mapped) +#define emap_init JEMALLOC_N(emap_init) +#define emap_merge_commit JEMALLOC_N(emap_merge_commit) +#define emap_merge_prepare JEMALLOC_N(emap_merge_prepare) +#define emap_register_boundary JEMALLOC_N(emap_register_boundary) +#define emap_register_interior JEMALLOC_N(emap_register_interior) +#define emap_release_edata JEMALLOC_N(emap_release_edata) +#define emap_remap JEMALLOC_N(emap_remap) +#define emap_split_commit JEMALLOC_N(emap_split_commit) +#define emap_split_prepare JEMALLOC_N(emap_split_prepare) +#define emap_try_acquire_edata_neighbor JEMALLOC_N(emap_try_acquire_edata_neighbor) +#define emap_try_acquire_edata_neighbor_expand JEMALLOC_N(emap_try_acquire_edata_neighbor_expand) +#define emap_update_edata_state JEMALLOC_N(emap_update_edata_state) +#define eset_fit JEMALLOC_N(eset_fit) +#define eset_init JEMALLOC_N(eset_init) +#define eset_insert JEMALLOC_N(eset_insert) +#define eset_nbytes_get JEMALLOC_N(eset_nbytes_get) +#define eset_nextents_get JEMALLOC_N(eset_nextents_get) +#define eset_npages_get JEMALLOC_N(eset_npages_get) +#define eset_remove JEMALLOC_N(eset_remove) +#define exp_grow_init JEMALLOC_N(exp_grow_init) +#define ecache_alloc JEMALLOC_N(ecache_alloc) +#define ecache_alloc_grow JEMALLOC_N(ecache_alloc_grow) +#define ecache_dalloc JEMALLOC_N(ecache_dalloc) +#define ecache_evict JEMALLOC_N(ecache_evict) #define extent_alloc_wrapper JEMALLOC_N(extent_alloc_wrapper) -#define extent_avail_any JEMALLOC_N(extent_avail_any) -#define extent_avail_empty JEMALLOC_N(extent_avail_empty) -#define extent_avail_first JEMALLOC_N(extent_avail_first) -#define extent_avail_insert JEMALLOC_N(extent_avail_insert) -#define extent_avail_new JEMALLOC_N(extent_avail_new) -#define extent_avail_remove JEMALLOC_N(extent_avail_remove) -#define extent_avail_remove_any JEMALLOC_N(extent_avail_remove_any) -#define extent_avail_remove_first JEMALLOC_N(extent_avail_remove_first) #define extent_boot JEMALLOC_N(extent_boot) #define extent_commit_wrapper JEMALLOC_N(extent_commit_wrapper) -#define extent_dalloc JEMALLOC_N(extent_dalloc) +#define extent_commit_zero JEMALLOC_N(extent_commit_zero) #define extent_dalloc_gap JEMALLOC_N(extent_dalloc_gap) #define extent_dalloc_wrapper JEMALLOC_N(extent_dalloc_wrapper) #define extent_decommit_wrapper JEMALLOC_N(extent_decommit_wrapper) #define extent_destroy_wrapper JEMALLOC_N(extent_destroy_wrapper) -#define extent_heap_any JEMALLOC_N(extent_heap_any) -#define extent_heap_empty JEMALLOC_N(extent_heap_empty) -#define extent_heap_first JEMALLOC_N(extent_heap_first) -#define extent_heap_insert JEMALLOC_N(extent_heap_insert) -#define extent_heap_new JEMALLOC_N(extent_heap_new) -#define extent_heap_remove JEMALLOC_N(extent_heap_remove) -#define extent_heap_remove_any JEMALLOC_N(extent_heap_remove_any) -#define extent_heap_remove_first JEMALLOC_N(extent_heap_remove_first) -#define extent_hooks_default JEMALLOC_N(extent_hooks_default) -#define extent_hooks_get JEMALLOC_N(extent_hooks_get) -#define extent_hooks_set JEMALLOC_N(extent_hooks_set) +#define extent_gdump_add JEMALLOC_N(extent_gdump_add) #define extent_merge_wrapper JEMALLOC_N(extent_merge_wrapper) -#define extent_mutex_pool JEMALLOC_N(extent_mutex_pool) #define extent_purge_forced_wrapper JEMALLOC_N(extent_purge_forced_wrapper) #define extent_purge_lazy_wrapper JEMALLOC_N(extent_purge_lazy_wrapper) +#define extent_record JEMALLOC_N(extent_record) +#define extent_sn_next JEMALLOC_N(extent_sn_next) #define extent_split_wrapper JEMALLOC_N(extent_split_wrapper) -#define extent_util_stats_get JEMALLOC_N(extent_util_stats_get) -#define extent_util_stats_verbose_get JEMALLOC_N(extent_util_stats_verbose_get) -#define extents_alloc JEMALLOC_N(extents_alloc) -#define extents_dalloc JEMALLOC_N(extents_dalloc) -#define extents_evict JEMALLOC_N(extents_evict) -#define extents_init JEMALLOC_N(extents_init) -#define extents_nbytes_get JEMALLOC_N(extents_nbytes_get) -#define extents_nextents_get JEMALLOC_N(extents_nextents_get) -#define extents_npages_get JEMALLOC_N(extents_npages_get) -#define extents_postfork_child JEMALLOC_N(extents_postfork_child) -#define extents_postfork_parent JEMALLOC_N(extents_postfork_parent) -#define extents_prefork JEMALLOC_N(extents_prefork) -#define extents_rtree JEMALLOC_N(extents_rtree) -#define extents_state_get JEMALLOC_N(extents_state_get) #define opt_lg_extent_max_active_fit JEMALLOC_N(opt_lg_extent_max_active_fit) #define dss_prec_names JEMALLOC_N(dss_prec_names) #define extent_alloc_dss JEMALLOC_N(extent_alloc_dss) @@ -216,24 +301,66 @@ #define extent_alloc_mmap JEMALLOC_N(extent_alloc_mmap) #define extent_dalloc_mmap JEMALLOC_N(extent_dalloc_mmap) #define opt_retain JEMALLOC_N(opt_retain) +#define fxp_parse JEMALLOC_N(fxp_parse) +#define fxp_print JEMALLOC_N(fxp_print) +#define opt_lg_san_uaf_align JEMALLOC_N(opt_lg_san_uaf_align) +#define opt_san_guard_large JEMALLOC_N(opt_san_guard_large) +#define opt_san_guard_small JEMALLOC_N(opt_san_guard_small) +#define san_cache_bin_nonfast_mask JEMALLOC_N(san_cache_bin_nonfast_mask) +#define san_check_stashed_ptrs JEMALLOC_N(san_check_stashed_ptrs) +#define san_guard_pages JEMALLOC_N(san_guard_pages) +#define san_init JEMALLOC_N(san_init) +#define san_unguard_pages JEMALLOC_N(san_unguard_pages) +#define san_unguard_pages_pre_destroy JEMALLOC_N(san_unguard_pages_pre_destroy) +#define tsd_san_init JEMALLOC_N(tsd_san_init) +#define san_bump_alloc JEMALLOC_N(san_bump_alloc) #define hook_boot JEMALLOC_N(hook_boot) #define hook_install JEMALLOC_N(hook_install) #define hook_invoke_alloc JEMALLOC_N(hook_invoke_alloc) #define hook_invoke_dalloc JEMALLOC_N(hook_invoke_dalloc) #define hook_invoke_expand JEMALLOC_N(hook_invoke_expand) #define hook_remove JEMALLOC_N(hook_remove) +#define hpa_central_extract JEMALLOC_N(hpa_central_extract) +#define hpa_central_init JEMALLOC_N(hpa_central_init) +#define hpa_shard_destroy JEMALLOC_N(hpa_shard_destroy) +#define hpa_shard_disable JEMALLOC_N(hpa_shard_disable) +#define hpa_shard_do_deferred_work JEMALLOC_N(hpa_shard_do_deferred_work) +#define hpa_shard_init JEMALLOC_N(hpa_shard_init) +#define hpa_shard_postfork_child JEMALLOC_N(hpa_shard_postfork_child) +#define hpa_shard_postfork_parent JEMALLOC_N(hpa_shard_postfork_parent) +#define hpa_shard_prefork3 JEMALLOC_N(hpa_shard_prefork3) +#define hpa_shard_prefork4 JEMALLOC_N(hpa_shard_prefork4) +#define hpa_shard_set_deferral_allowed JEMALLOC_N(hpa_shard_set_deferral_allowed) +#define hpa_shard_stats_accum JEMALLOC_N(hpa_shard_stats_accum) +#define hpa_shard_stats_merge JEMALLOC_N(hpa_shard_stats_merge) +#define hpa_supported JEMALLOC_N(hpa_supported) +#define hpa_hooks_default JEMALLOC_N(hpa_hooks_default) +#define hpdata_age_heap_any JEMALLOC_N(hpdata_age_heap_any) +#define hpdata_age_heap_empty JEMALLOC_N(hpdata_age_heap_empty) +#define hpdata_age_heap_first JEMALLOC_N(hpdata_age_heap_first) +#define hpdata_age_heap_insert JEMALLOC_N(hpdata_age_heap_insert) +#define hpdata_age_heap_new JEMALLOC_N(hpdata_age_heap_new) +#define hpdata_age_heap_remove JEMALLOC_N(hpdata_age_heap_remove) +#define hpdata_age_heap_remove_any JEMALLOC_N(hpdata_age_heap_remove_any) +#define hpdata_age_heap_remove_first JEMALLOC_N(hpdata_age_heap_remove_first) +#define hpdata_dehugify JEMALLOC_N(hpdata_dehugify) +#define hpdata_hugify JEMALLOC_N(hpdata_hugify) +#define hpdata_init JEMALLOC_N(hpdata_init) +#define hpdata_purge_begin JEMALLOC_N(hpdata_purge_begin) +#define hpdata_purge_end JEMALLOC_N(hpdata_purge_end) +#define hpdata_purge_next JEMALLOC_N(hpdata_purge_next) +#define hpdata_reserve_alloc JEMALLOC_N(hpdata_reserve_alloc) +#define hpdata_unreserve JEMALLOC_N(hpdata_unreserve) +#define inspect_extent_util_stats_get JEMALLOC_N(inspect_extent_util_stats_get) +#define inspect_extent_util_stats_verbose_get JEMALLOC_N(inspect_extent_util_stats_verbose_get) #define large_dalloc JEMALLOC_N(large_dalloc) #define large_dalloc_finish JEMALLOC_N(large_dalloc_finish) -#define large_dalloc_junk JEMALLOC_N(large_dalloc_junk) -#define large_dalloc_maybe_junk JEMALLOC_N(large_dalloc_maybe_junk) -#define large_dalloc_prep_junked_locked JEMALLOC_N(large_dalloc_prep_junked_locked) +#define large_dalloc_prep_locked JEMALLOC_N(large_dalloc_prep_locked) #define large_malloc JEMALLOC_N(large_malloc) #define large_palloc JEMALLOC_N(large_palloc) -#define large_prof_alloc_time_get JEMALLOC_N(large_prof_alloc_time_get) -#define large_prof_alloc_time_set JEMALLOC_N(large_prof_alloc_time_set) -#define large_prof_tctx_get JEMALLOC_N(large_prof_tctx_get) +#define large_prof_info_get JEMALLOC_N(large_prof_info_get) +#define large_prof_info_set JEMALLOC_N(large_prof_info_set) #define large_prof_tctx_reset JEMALLOC_N(large_prof_tctx_reset) -#define large_prof_tctx_set JEMALLOC_N(large_prof_tctx_set) #define large_ralloc JEMALLOC_N(large_ralloc) #define large_ralloc_no_move JEMALLOC_N(large_ralloc_no_move) #define large_salloc JEMALLOC_N(large_salloc) @@ -248,14 +375,17 @@ #define malloc_vcprintf JEMALLOC_N(malloc_vcprintf) #define malloc_vsnprintf JEMALLOC_N(malloc_vsnprintf) #define malloc_write JEMALLOC_N(malloc_write) +#define wrtmessage JEMALLOC_N(wrtmessage) +#define wrtmessage_1_0 JEMALLOC_N(wrtmessage_1_0) #define malloc_mutex_boot JEMALLOC_N(malloc_mutex_boot) +#define malloc_mutex_first_thread JEMALLOC_N(malloc_mutex_first_thread) #define malloc_mutex_init JEMALLOC_N(malloc_mutex_init) #define malloc_mutex_lock_slow JEMALLOC_N(malloc_mutex_lock_slow) #define malloc_mutex_postfork_child JEMALLOC_N(malloc_mutex_postfork_child) #define malloc_mutex_postfork_parent JEMALLOC_N(malloc_mutex_postfork_parent) #define malloc_mutex_prefork JEMALLOC_N(malloc_mutex_prefork) #define malloc_mutex_prof_data_reset JEMALLOC_N(malloc_mutex_prof_data_reset) -#define mutex_pool_init JEMALLOC_N(mutex_pool_init) +#define opt_mutex_max_spin JEMALLOC_N(opt_mutex_max_spin) #define nstime_add JEMALLOC_N(nstime_add) #define nstime_compare JEMALLOC_N(nstime_compare) #define nstime_copy JEMALLOC_N(nstime_copy) @@ -265,14 +395,56 @@ #define nstime_imultiply JEMALLOC_N(nstime_imultiply) #define nstime_init JEMALLOC_N(nstime_init) #define nstime_init2 JEMALLOC_N(nstime_init2) +#define nstime_init_update JEMALLOC_N(nstime_init_update) #define nstime_isubtract JEMALLOC_N(nstime_isubtract) #define nstime_monotonic JEMALLOC_N(nstime_monotonic) #define nstime_msec JEMALLOC_N(nstime_msec) #define nstime_ns JEMALLOC_N(nstime_ns) +#define nstime_ns_since JEMALLOC_N(nstime_ns_since) #define nstime_nsec JEMALLOC_N(nstime_nsec) +#define nstime_prof_init_update JEMALLOC_N(nstime_prof_init_update) +#define nstime_prof_update JEMALLOC_N(nstime_prof_update) #define nstime_sec JEMALLOC_N(nstime_sec) #define nstime_subtract JEMALLOC_N(nstime_subtract) #define nstime_update JEMALLOC_N(nstime_update) +#define opt_prof_time_res JEMALLOC_N(opt_prof_time_res) +#define prof_time_res_mode_names JEMALLOC_N(prof_time_res_mode_names) +#define pa_alloc JEMALLOC_N(pa_alloc) +#define pa_central_init JEMALLOC_N(pa_central_init) +#define pa_dalloc JEMALLOC_N(pa_dalloc) +#define pa_decay_ms_get JEMALLOC_N(pa_decay_ms_get) +#define pa_decay_ms_set JEMALLOC_N(pa_decay_ms_set) +#define pa_expand JEMALLOC_N(pa_expand) +#define pa_shard_destroy JEMALLOC_N(pa_shard_destroy) +#define pa_shard_disable_hpa JEMALLOC_N(pa_shard_disable_hpa) +#define pa_shard_do_deferred_work JEMALLOC_N(pa_shard_do_deferred_work) +#define pa_shard_enable_hpa JEMALLOC_N(pa_shard_enable_hpa) +#define pa_shard_init JEMALLOC_N(pa_shard_init) +#define pa_shard_reset JEMALLOC_N(pa_shard_reset) +#define pa_shard_retain_grow_limit_get_set JEMALLOC_N(pa_shard_retain_grow_limit_get_set) +#define pa_shard_set_deferral_allowed JEMALLOC_N(pa_shard_set_deferral_allowed) +#define pa_shard_time_until_deferred_work JEMALLOC_N(pa_shard_time_until_deferred_work) +#define pa_shrink JEMALLOC_N(pa_shrink) +#define pa_shard_basic_stats_merge JEMALLOC_N(pa_shard_basic_stats_merge) +#define pa_shard_mtx_stats_read JEMALLOC_N(pa_shard_mtx_stats_read) +#define pa_shard_postfork_child JEMALLOC_N(pa_shard_postfork_child) +#define pa_shard_postfork_parent JEMALLOC_N(pa_shard_postfork_parent) +#define pa_shard_prefork0 JEMALLOC_N(pa_shard_prefork0) +#define pa_shard_prefork2 JEMALLOC_N(pa_shard_prefork2) +#define pa_shard_prefork3 JEMALLOC_N(pa_shard_prefork3) +#define pa_shard_prefork4 JEMALLOC_N(pa_shard_prefork4) +#define pa_shard_prefork5 JEMALLOC_N(pa_shard_prefork5) +#define pa_shard_stats_merge JEMALLOC_N(pa_shard_stats_merge) +#define pai_alloc_batch_default JEMALLOC_N(pai_alloc_batch_default) +#define pai_dalloc_batch_default JEMALLOC_N(pai_dalloc_batch_default) +#define pac_decay_all JEMALLOC_N(pac_decay_all) +#define pac_decay_ms_get JEMALLOC_N(pac_decay_ms_get) +#define pac_decay_ms_set JEMALLOC_N(pac_decay_ms_set) +#define pac_destroy JEMALLOC_N(pac_destroy) +#define pac_init JEMALLOC_N(pac_init) +#define pac_maybe_decay_purge JEMALLOC_N(pac_maybe_decay_purge) +#define pac_reset JEMALLOC_N(pac_reset) +#define pac_retain_grow_limit_get_set JEMALLOC_N(pac_retain_grow_limit_get_set) #define init_system_thp_mode JEMALLOC_N(init_system_thp_mode) #define opt_thp JEMALLOC_N(opt_thp) #define pages_boot JEMALLOC_N(pages_boot) @@ -282,14 +454,23 @@ #define pages_dontdump JEMALLOC_N(pages_dontdump) #define pages_huge JEMALLOC_N(pages_huge) #define pages_map JEMALLOC_N(pages_map) +#define pages_mark_guards JEMALLOC_N(pages_mark_guards) #define pages_nohuge JEMALLOC_N(pages_nohuge) #define pages_purge_forced JEMALLOC_N(pages_purge_forced) #define pages_purge_lazy JEMALLOC_N(pages_purge_lazy) #define pages_set_thp_state JEMALLOC_N(pages_set_thp_state) #define pages_unmap JEMALLOC_N(pages_unmap) +#define pages_unmark_guards JEMALLOC_N(pages_unmark_guards) #define thp_mode_names JEMALLOC_N(thp_mode_names) -#define bt2gctx_mtx JEMALLOC_N(bt2gctx_mtx) -#define bt_init JEMALLOC_N(bt_init) +#define peak_alloc_event_handler JEMALLOC_N(peak_alloc_event_handler) +#define peak_alloc_new_event_wait JEMALLOC_N(peak_alloc_new_event_wait) +#define peak_alloc_postponed_event_wait JEMALLOC_N(peak_alloc_postponed_event_wait) +#define peak_dalloc_event_handler JEMALLOC_N(peak_dalloc_event_handler) +#define peak_dalloc_new_event_wait JEMALLOC_N(peak_dalloc_new_event_wait) +#define peak_dalloc_postponed_event_wait JEMALLOC_N(peak_dalloc_postponed_event_wait) +#define peak_event_max JEMALLOC_N(peak_event_max) +#define peak_event_update JEMALLOC_N(peak_event_update) +#define peak_event_zero JEMALLOC_N(peak_event_zero) #define lg_prof_sample JEMALLOC_N(lg_prof_sample) #define opt_lg_prof_interval JEMALLOC_N(opt_lg_prof_interval) #define opt_lg_prof_sample JEMALLOC_N(opt_lg_prof_sample) @@ -299,20 +480,25 @@ #define opt_prof_final JEMALLOC_N(opt_prof_final) #define opt_prof_gdump JEMALLOC_N(opt_prof_gdump) #define opt_prof_leak JEMALLOC_N(opt_prof_leak) -#define opt_prof_log JEMALLOC_N(opt_prof_log) +#define opt_prof_leak_error JEMALLOC_N(opt_prof_leak_error) #define opt_prof_prefix JEMALLOC_N(opt_prof_prefix) +#define opt_prof_sys_thread_name JEMALLOC_N(opt_prof_sys_thread_name) #define opt_prof_thread_active_init JEMALLOC_N(opt_prof_thread_active_init) -#define prof_accum_init JEMALLOC_N(prof_accum_init) -#define prof_active JEMALLOC_N(prof_active) +#define opt_prof_unbias JEMALLOC_N(opt_prof_unbias) #define prof_active_get JEMALLOC_N(prof_active_get) #define prof_active_set JEMALLOC_N(prof_active_set) +#define prof_active_state JEMALLOC_N(prof_active_state) #define prof_alloc_rollback JEMALLOC_N(prof_alloc_rollback) -#define prof_backtrace JEMALLOC_N(prof_backtrace) +#define prof_backtrace_hook JEMALLOC_N(prof_backtrace_hook) +#define prof_backtrace_hook_get JEMALLOC_N(prof_backtrace_hook_get) +#define prof_backtrace_hook_set JEMALLOC_N(prof_backtrace_hook_set) #define prof_boot0 JEMALLOC_N(prof_boot0) #define prof_boot1 JEMALLOC_N(prof_boot1) #define prof_boot2 JEMALLOC_N(prof_boot2) -#define prof_dump_header JEMALLOC_N(prof_dump_header) -#define prof_dump_open JEMALLOC_N(prof_dump_open) +#define prof_booted JEMALLOC_N(prof_booted) +#define prof_dump_hook JEMALLOC_N(prof_dump_hook) +#define prof_dump_hook_get JEMALLOC_N(prof_dump_hook_get) +#define prof_dump_hook_set JEMALLOC_N(prof_dump_hook_set) #define prof_free_sampled_object JEMALLOC_N(prof_free_sampled_object) #define prof_gdump JEMALLOC_N(prof_gdump) #define prof_gdump_get JEMALLOC_N(prof_gdump_get) @@ -320,18 +506,16 @@ #define prof_gdump_val JEMALLOC_N(prof_gdump_val) #define prof_idump JEMALLOC_N(prof_idump) #define prof_interval JEMALLOC_N(prof_interval) -#define prof_log_start JEMALLOC_N(prof_log_start) -#define prof_log_stop JEMALLOC_N(prof_log_stop) -#define prof_logging_state JEMALLOC_N(prof_logging_state) -#define prof_lookup JEMALLOC_N(prof_lookup) #define prof_malloc_sample_object JEMALLOC_N(prof_malloc_sample_object) #define prof_mdump JEMALLOC_N(prof_mdump) #define prof_postfork_child JEMALLOC_N(prof_postfork_child) #define prof_postfork_parent JEMALLOC_N(prof_postfork_parent) #define prof_prefork0 JEMALLOC_N(prof_prefork0) #define prof_prefork1 JEMALLOC_N(prof_prefork1) -#define prof_reset JEMALLOC_N(prof_reset) -#define prof_sample_threshold_update JEMALLOC_N(prof_sample_threshold_update) +#define prof_sample_event_handler JEMALLOC_N(prof_sample_event_handler) +#define prof_sample_new_event_wait JEMALLOC_N(prof_sample_new_event_wait) +#define prof_sample_postponed_event_wait JEMALLOC_N(prof_sample_postponed_event_wait) +#define prof_tctx_create JEMALLOC_N(prof_tctx_create) #define prof_tdata_cleanup JEMALLOC_N(prof_tdata_cleanup) #define prof_tdata_init JEMALLOC_N(prof_tdata_init) #define prof_tdata_reinit JEMALLOC_N(prof_tdata_reinit) @@ -341,42 +525,157 @@ #define prof_thread_active_set JEMALLOC_N(prof_thread_active_set) #define prof_thread_name_get JEMALLOC_N(prof_thread_name_get) #define prof_thread_name_set JEMALLOC_N(prof_thread_name_set) +#define bt2gctx_mtx JEMALLOC_N(bt2gctx_mtx) +#define gctx_locks JEMALLOC_N(gctx_locks) +#define prof_bt_count JEMALLOC_N(prof_bt_count) +#define prof_bt_hash JEMALLOC_N(prof_bt_hash) +#define prof_bt_keycomp JEMALLOC_N(prof_bt_keycomp) +#define prof_cnt_all JEMALLOC_N(prof_cnt_all) +#define prof_data_init JEMALLOC_N(prof_data_init) +#define prof_dump_impl JEMALLOC_N(prof_dump_impl) +#define prof_dump_mtx JEMALLOC_N(prof_dump_mtx) +#define prof_lookup JEMALLOC_N(prof_lookup) +#define prof_reset JEMALLOC_N(prof_reset) +#define prof_shifted_unbiased_cnt JEMALLOC_N(prof_shifted_unbiased_cnt) +#define prof_tctx_try_destroy JEMALLOC_N(prof_tctx_try_destroy) +#define prof_tdata_count JEMALLOC_N(prof_tdata_count) +#define prof_tdata_detach JEMALLOC_N(prof_tdata_detach) +#define prof_tdata_init_impl JEMALLOC_N(prof_tdata_init_impl) +#define prof_thread_name_alloc JEMALLOC_N(prof_thread_name_alloc) +#define prof_thread_name_set_impl JEMALLOC_N(prof_thread_name_set_impl) +#define prof_unbias_map_init JEMALLOC_N(prof_unbias_map_init) +#define prof_unbiased_sz JEMALLOC_N(prof_unbiased_sz) +#define tdata_locks JEMALLOC_N(tdata_locks) +#define tdatas_mtx JEMALLOC_N(tdatas_mtx) +#define log_mtx JEMALLOC_N(log_mtx) +#define opt_prof_log JEMALLOC_N(opt_prof_log) +#define prof_log_alloc_count JEMALLOC_N(prof_log_alloc_count) +#define prof_log_bt_count JEMALLOC_N(prof_log_bt_count) +#define prof_log_dummy_set JEMALLOC_N(prof_log_dummy_set) +#define prof_log_init JEMALLOC_N(prof_log_init) +#define prof_log_is_logging JEMALLOC_N(prof_log_is_logging) +#define prof_log_rep_check JEMALLOC_N(prof_log_rep_check) +#define prof_log_start JEMALLOC_N(prof_log_start) +#define prof_log_stop JEMALLOC_N(prof_log_stop) +#define prof_log_thr_count JEMALLOC_N(prof_log_thr_count) +#define prof_logging_state JEMALLOC_N(prof_logging_state) +#define prof_try_log JEMALLOC_N(prof_try_log) +#define edata_prof_recent_alloc_get_no_lock_test JEMALLOC_N(edata_prof_recent_alloc_get_no_lock_test) +#define edata_prof_recent_alloc_init JEMALLOC_N(edata_prof_recent_alloc_init) +#define opt_prof_recent_alloc_max JEMALLOC_N(opt_prof_recent_alloc_max) +#define prof_recent_alloc JEMALLOC_N(prof_recent_alloc) +#define prof_recent_alloc_dump JEMALLOC_N(prof_recent_alloc_dump) +#define prof_recent_alloc_edata_get_no_lock_test JEMALLOC_N(prof_recent_alloc_edata_get_no_lock_test) +#define prof_recent_alloc_list JEMALLOC_N(prof_recent_alloc_list) +#define prof_recent_alloc_max_ctl_read JEMALLOC_N(prof_recent_alloc_max_ctl_read) +#define prof_recent_alloc_max_ctl_write JEMALLOC_N(prof_recent_alloc_max_ctl_write) +#define prof_recent_alloc_mtx JEMALLOC_N(prof_recent_alloc_mtx) +#define prof_recent_alloc_prepare JEMALLOC_N(prof_recent_alloc_prepare) +#define prof_recent_alloc_reset JEMALLOC_N(prof_recent_alloc_reset) +#define prof_recent_dump_mtx JEMALLOC_N(prof_recent_dump_mtx) +#define prof_recent_init JEMALLOC_N(prof_recent_init) +#define opt_prof_stats JEMALLOC_N(opt_prof_stats) +#define prof_stats_dec JEMALLOC_N(prof_stats_dec) +#define prof_stats_get_accum JEMALLOC_N(prof_stats_get_accum) +#define prof_stats_get_live JEMALLOC_N(prof_stats_get_live) +#define prof_stats_inc JEMALLOC_N(prof_stats_inc) +#define prof_stats_mtx JEMALLOC_N(prof_stats_mtx) +#define bt_init JEMALLOC_N(bt_init) +#define prof_backtrace JEMALLOC_N(prof_backtrace) +#define prof_base JEMALLOC_N(prof_base) +#define prof_do_mock JEMALLOC_N(prof_do_mock) +#define prof_dump_filename_mtx JEMALLOC_N(prof_dump_filename_mtx) +#define prof_dump_open_file JEMALLOC_N(prof_dump_open_file) +#define prof_dump_open_maps JEMALLOC_N(prof_dump_open_maps) +#define prof_dump_write_file JEMALLOC_N(prof_dump_write_file) +#define prof_fdump_impl JEMALLOC_N(prof_fdump_impl) +#define prof_gdump_impl JEMALLOC_N(prof_gdump_impl) +#define prof_get_default_filename JEMALLOC_N(prof_get_default_filename) +#define prof_getpid JEMALLOC_N(prof_getpid) +#define prof_hooks_init JEMALLOC_N(prof_hooks_init) +#define prof_idump_impl JEMALLOC_N(prof_idump_impl) +#define prof_mdump_impl JEMALLOC_N(prof_mdump_impl) +#define prof_prefix_set JEMALLOC_N(prof_prefix_set) +#define prof_sys_thread_name_fetch JEMALLOC_N(prof_sys_thread_name_fetch) +#define prof_sys_thread_name_read JEMALLOC_N(prof_sys_thread_name_read) +#define prof_unwind_init JEMALLOC_N(prof_unwind_init) +#define psset_init JEMALLOC_N(psset_init) +#define psset_insert JEMALLOC_N(psset_insert) +#define psset_pick_alloc JEMALLOC_N(psset_pick_alloc) +#define psset_pick_hugify JEMALLOC_N(psset_pick_hugify) +#define psset_pick_purge JEMALLOC_N(psset_pick_purge) +#define psset_remove JEMALLOC_N(psset_remove) +#define psset_stats_accum JEMALLOC_N(psset_stats_accum) +#define psset_update_begin JEMALLOC_N(psset_update_begin) +#define psset_update_end JEMALLOC_N(psset_update_end) #define rtree_ctx_data_init JEMALLOC_N(rtree_ctx_data_init) -#define rtree_leaf_alloc JEMALLOC_N(rtree_leaf_alloc) -#define rtree_leaf_dalloc JEMALLOC_N(rtree_leaf_dalloc) #define rtree_leaf_elm_lookup_hard JEMALLOC_N(rtree_leaf_elm_lookup_hard) #define rtree_new JEMALLOC_N(rtree_new) -#define rtree_node_alloc JEMALLOC_N(rtree_node_alloc) -#define rtree_node_dalloc JEMALLOC_N(rtree_node_dalloc) #define safety_check_fail JEMALLOC_N(safety_check_fail) +#define safety_check_fail_sized_dealloc JEMALLOC_N(safety_check_fail_sized_dealloc) #define safety_check_set_abort JEMALLOC_N(safety_check_set_abort) +#define reg_size_compute JEMALLOC_N(reg_size_compute) +#define sc_boot JEMALLOC_N(sc_boot) +#define sc_data_init JEMALLOC_N(sc_data_init) +#define sc_data_update_slab_size JEMALLOC_N(sc_data_update_slab_size) +#define sec_disable JEMALLOC_N(sec_disable) +#define sec_flush JEMALLOC_N(sec_flush) +#define sec_init JEMALLOC_N(sec_init) +#define sec_mutex_stats_read JEMALLOC_N(sec_mutex_stats_read) +#define sec_postfork_child JEMALLOC_N(sec_postfork_child) +#define sec_postfork_parent JEMALLOC_N(sec_postfork_parent) +#define sec_prefork2 JEMALLOC_N(sec_prefork2) +#define sec_stats_merge JEMALLOC_N(sec_stats_merge) #define arena_mutex_names JEMALLOC_N(arena_mutex_names) #define global_mutex_names JEMALLOC_N(global_mutex_names) +#define opt_stats_interval JEMALLOC_N(opt_stats_interval) +#define opt_stats_interval_opts JEMALLOC_N(opt_stats_interval_opts) #define opt_stats_print JEMALLOC_N(opt_stats_print) #define opt_stats_print_opts JEMALLOC_N(opt_stats_print_opts) +#define stats_boot JEMALLOC_N(stats_boot) +#define stats_interval_event_handler JEMALLOC_N(stats_interval_event_handler) +#define stats_interval_new_event_wait JEMALLOC_N(stats_interval_new_event_wait) +#define stats_interval_postponed_event_wait JEMALLOC_N(stats_interval_postponed_event_wait) +#define stats_postfork_child JEMALLOC_N(stats_postfork_child) +#define stats_postfork_parent JEMALLOC_N(stats_postfork_parent) +#define stats_prefork JEMALLOC_N(stats_prefork) #define stats_print JEMALLOC_N(stats_print) -#define sc_boot JEMALLOC_N(sc_boot) -#define sc_data_global JEMALLOC_N(sc_data_global) -#define sc_data_init JEMALLOC_N(sc_data_init) -#define sc_data_update_slab_size JEMALLOC_N(sc_data_update_slab_size) #define sz_boot JEMALLOC_N(sz_boot) #define sz_index2size_tab JEMALLOC_N(sz_index2size_tab) +#define sz_large_pad JEMALLOC_N(sz_large_pad) #define sz_pind2sz_tab JEMALLOC_N(sz_pind2sz_tab) +#define sz_psz_quantize_ceil JEMALLOC_N(sz_psz_quantize_ceil) +#define sz_psz_quantize_floor JEMALLOC_N(sz_psz_quantize_floor) #define sz_size2index_tab JEMALLOC_N(sz_size2index_tab) #define nhbins JEMALLOC_N(nhbins) -#define opt_lg_tcache_max JEMALLOC_N(opt_lg_tcache_max) +#define opt_lg_tcache_flush_large_div JEMALLOC_N(opt_lg_tcache_flush_large_div) +#define opt_lg_tcache_flush_small_div JEMALLOC_N(opt_lg_tcache_flush_small_div) +#define opt_lg_tcache_nslots_mul JEMALLOC_N(opt_lg_tcache_nslots_mul) #define opt_tcache JEMALLOC_N(opt_tcache) +#define opt_tcache_gc_delay_bytes JEMALLOC_N(opt_tcache_gc_delay_bytes) +#define opt_tcache_gc_incr_bytes JEMALLOC_N(opt_tcache_gc_incr_bytes) +#define opt_tcache_max JEMALLOC_N(opt_tcache_max) +#define opt_tcache_nslots_large JEMALLOC_N(opt_tcache_nslots_large) +#define opt_tcache_nslots_small_max JEMALLOC_N(opt_tcache_nslots_small_max) +#define opt_tcache_nslots_small_min JEMALLOC_N(opt_tcache_nslots_small_min) #define tcache_alloc_small_hard JEMALLOC_N(tcache_alloc_small_hard) #define tcache_arena_associate JEMALLOC_N(tcache_arena_associate) #define tcache_arena_reassociate JEMALLOC_N(tcache_arena_reassociate) +#define tcache_assert_initialized JEMALLOC_N(tcache_assert_initialized) #define tcache_bin_flush_large JEMALLOC_N(tcache_bin_flush_large) #define tcache_bin_flush_small JEMALLOC_N(tcache_bin_flush_small) +#define tcache_bin_flush_stashed JEMALLOC_N(tcache_bin_flush_stashed) #define tcache_bin_info JEMALLOC_N(tcache_bin_info) #define tcache_boot JEMALLOC_N(tcache_boot) #define tcache_cleanup JEMALLOC_N(tcache_cleanup) #define tcache_create_explicit JEMALLOC_N(tcache_create_explicit) -#define tcache_event_hard JEMALLOC_N(tcache_event_hard) #define tcache_flush JEMALLOC_N(tcache_flush) +#define tcache_gc_dalloc_event_handler JEMALLOC_N(tcache_gc_dalloc_event_handler) +#define tcache_gc_dalloc_new_event_wait JEMALLOC_N(tcache_gc_dalloc_new_event_wait) +#define tcache_gc_dalloc_postponed_event_wait JEMALLOC_N(tcache_gc_dalloc_postponed_event_wait) +#define tcache_gc_event_handler JEMALLOC_N(tcache_gc_event_handler) +#define tcache_gc_new_event_wait JEMALLOC_N(tcache_gc_new_event_wait) +#define tcache_gc_postponed_event_wait JEMALLOC_N(tcache_gc_postponed_event_wait) #define tcache_maxclass JEMALLOC_N(tcache_maxclass) #define tcache_postfork_child JEMALLOC_N(tcache_postfork_child) #define tcache_postfork_parent JEMALLOC_N(tcache_postfork_parent) @@ -391,9 +690,13 @@ #define tsd_tcache_enabled_data_init JEMALLOC_N(tsd_tcache_enabled_data_init) #define test_hooks_arena_new_hook JEMALLOC_N(test_hooks_arena_new_hook) #define test_hooks_libc_hook JEMALLOC_N(test_hooks_libc_hook) +#define te_assert_invariants_debug JEMALLOC_N(te_assert_invariants_debug) +#define te_event_trigger JEMALLOC_N(te_event_trigger) +#define te_recompute_fast_threshold JEMALLOC_N(te_recompute_fast_threshold) +#define tsd_te_init JEMALLOC_N(tsd_te_init) +#define ticker_geom_table JEMALLOC_N(ticker_geom_table) #define malloc_tsd_boot0 JEMALLOC_N(malloc_tsd_boot0) #define malloc_tsd_boot1 JEMALLOC_N(malloc_tsd_boot1) -#define malloc_tsd_cleanup_register JEMALLOC_N(malloc_tsd_cleanup_register) #define malloc_tsd_dalloc JEMALLOC_N(malloc_tsd_dalloc) #define malloc_tsd_malloc JEMALLOC_N(malloc_tsd_malloc) #define tsd_booted JEMALLOC_N(tsd_booted) diff --git a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/public_namespace.h b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/public_namespace.h index 78d5c66377c3..6363f085f34b 100644 --- a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/public_namespace.h +++ b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/public_namespace.h @@ -7,11 +7,12 @@ #define je_mallctlnametomib JEMALLOC_N(mallctlnametomib) #define je_malloc JEMALLOC_N(malloc) #define je_malloc_conf JEMALLOC_N(malloc_conf) +#define je_malloc_conf_2_conf_harder JEMALLOC_N(malloc_conf_2_conf_harder) #define je_malloc_message JEMALLOC_N(malloc_message) #define je_malloc_stats_print JEMALLOC_N(malloc_stats_print) #define je_malloc_usable_size JEMALLOC_N(malloc_usable_size) #define je_mallocx JEMALLOC_N(mallocx) -#define je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 JEMALLOC_N(smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756) +#define je_smallocx_54eaed1d8b56b1aa528be3bdd1877e59c56fa90c JEMALLOC_N(smallocx_54eaed1d8b56b1aa528be3bdd1877e59c56fa90c) #define je_nallocx JEMALLOC_N(nallocx) #define je_posix_memalign JEMALLOC_N(posix_memalign) #define je_rallocx JEMALLOC_N(rallocx) @@ -19,4 +20,5 @@ #define je_sallocx JEMALLOC_N(sallocx) #define je_sdallocx JEMALLOC_N(sdallocx) #define je_xallocx JEMALLOC_N(xallocx) +#define je_memalign JEMALLOC_N(memalign) #define je_valloc JEMALLOC_N(valloc) diff --git a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc.h b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc.h index c3fa08e83f22..f9ed38656a87 100644 --- a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc.h +++ b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc.h @@ -19,6 +19,12 @@ extern "C" { /* Defined if format(printf, ...) attribute is supported. */ #define JEMALLOC_HAVE_ATTR_FORMAT_PRINTF +/* Defined if fallthrough attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_FALLTHROUGH + +/* Defined if cold attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_COLD + /* * Define overrides for non-standard allocator-related functions if they are * present on the system. @@ -68,11 +74,12 @@ extern "C" { # define je_mallctlnametomib mallctlnametomib # define je_malloc malloc # define je_malloc_conf malloc_conf +# define je_malloc_conf_2_conf_harder malloc_conf_2_conf_harder # define je_malloc_message malloc_message # define je_malloc_stats_print malloc_stats_print # define je_malloc_usable_size malloc_usable_size # define je_mallocx mallocx -# define je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 +# define je_smallocx_54eaed1d8b56b1aa528be3bdd1877e59c56fa90c smallocx_54eaed1d8b56b1aa528be3bdd1877e59c56fa90c # define je_nallocx nallocx # define je_posix_memalign posix_memalign # define je_rallocx rallocx @@ -80,6 +87,7 @@ extern "C" { # define je_sallocx sallocx # define je_sdallocx sdallocx # define je_xallocx xallocx +# define je_memalign memalign # define je_valloc valloc #endif @@ -91,13 +99,13 @@ extern "C" { #include <limits.h> #include <strings.h> -#define JEMALLOC_VERSION "5.2.1-0-gea6b3e973b477b8061e0076bb257dbd7f3faa756" +#define JEMALLOC_VERSION "5.3.0-0-g54eaed1d8b56b1aa528be3bdd1877e59c56fa90c" #define JEMALLOC_VERSION_MAJOR 5 -#define JEMALLOC_VERSION_MINOR 2 -#define JEMALLOC_VERSION_BUGFIX 1 +#define JEMALLOC_VERSION_MINOR 3 +#define JEMALLOC_VERSION_BUGFIX 0 #define JEMALLOC_VERSION_NREV 0 -#define JEMALLOC_VERSION_GID "ea6b3e973b477b8061e0076bb257dbd7f3faa756" -#define JEMALLOC_VERSION_GID_IDENT ea6b3e973b477b8061e0076bb257dbd7f3faa756 +#define JEMALLOC_VERSION_GID "54eaed1d8b56b1aa528be3bdd1877e59c56fa90c" +#define JEMALLOC_VERSION_GID_IDENT 54eaed1d8b56b1aa528be3bdd1877e59c56fa90c #define MALLOCX_LG_ALIGN(la) ((int)(la)) #if LG_SIZEOF_PTR == 2 @@ -158,6 +166,7 @@ extern "C" { # endif # define JEMALLOC_FORMAT_ARG(i) # define JEMALLOC_FORMAT_PRINTF(s, i) +# define JEMALLOC_FALLTHROUGH # define JEMALLOC_NOINLINE __declspec(noinline) # ifdef __cplusplus # define JEMALLOC_NOTHROW __declspec(nothrow) @@ -171,6 +180,7 @@ extern "C" { # else # define JEMALLOC_ALLOCATOR # endif +# define JEMALLOC_COLD #elif defined(JEMALLOC_HAVE_ATTR) # define JEMALLOC_ATTR(s) __attribute__((s)) # define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s)) @@ -196,11 +206,21 @@ extern "C" { # else # define JEMALLOC_FORMAT_PRINTF(s, i) # endif +# ifdef JEMALLOC_HAVE_ATTR_FALLTHROUGH +# define JEMALLOC_FALLTHROUGH JEMALLOC_ATTR(fallthrough) +# else +# define JEMALLOC_FALLTHROUGH +# endif # define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline) # define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow) # define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s)) # define JEMALLOC_RESTRICT_RETURN # define JEMALLOC_ALLOCATOR +# ifdef JEMALLOC_HAVE_ATTR_COLD +# define JEMALLOC_COLD JEMALLOC_ATTR(__cold__) +# else +# define JEMALLOC_COLD +# endif #else # define JEMALLOC_ATTR(s) # define JEMALLOC_ALIGNED(s) @@ -208,11 +228,19 @@ extern "C" { # define JEMALLOC_ALLOC_SIZE2(s1, s2) # define JEMALLOC_EXPORT # define JEMALLOC_FORMAT_PRINTF(s, i) +# define JEMALLOC_FALLTHROUGH # define JEMALLOC_NOINLINE # define JEMALLOC_NOTHROW # define JEMALLOC_SECTION(s) # define JEMALLOC_RESTRICT_RETURN # define JEMALLOC_ALLOCATOR +# define JEMALLOC_COLD +#endif + +#if (defined(__APPLE__) || defined(__FreeBSD__)) && !defined(JEMALLOC_NO_RENAME) +# define JEMALLOC_SYS_NOTHROW +#else +# define JEMALLOC_SYS_NOTHROW JEMALLOC_NOTHROW #endif /* @@ -225,21 +253,22 @@ extern JEMALLOC_EXPORT void (*je_malloc_message)(void *cbopaque, const char *s); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN - void JEMALLOC_NOTHROW *je_malloc(size_t size) + void JEMALLOC_SYS_NOTHROW *je_malloc(size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN - void JEMALLOC_NOTHROW *je_calloc(size_t num, size_t size) + void JEMALLOC_SYS_NOTHROW *je_calloc(size_t num, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2); -JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_posix_memalign(void **memptr, - size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1)); +JEMALLOC_EXPORT int JEMALLOC_SYS_NOTHROW je_posix_memalign( + void **memptr, size_t alignment, size_t size) JEMALLOC_CXX_THROW + JEMALLOC_ATTR(nonnull(1)); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN - void JEMALLOC_NOTHROW *je_aligned_alloc(size_t alignment, + void JEMALLOC_SYS_NOTHROW *je_aligned_alloc(size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2); JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN - void JEMALLOC_NOTHROW *je_realloc(void *ptr, size_t size) + void JEMALLOC_SYS_NOTHROW *je_realloc(void *ptr, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2); -JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_free(void *ptr) +JEMALLOC_EXPORT void JEMALLOC_SYS_NOTHROW je_free(void *ptr) JEMALLOC_CXX_THROW; JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN @@ -269,16 +298,20 @@ JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_malloc_stats_print( const char *opts); JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_malloc_usable_size( JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW; +#ifdef JEMALLOC_HAVE_MALLOC_SIZE +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_malloc_size( + const void *ptr); +#endif #ifdef JEMALLOC_OVERRIDE_MEMALIGN JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN - void JEMALLOC_NOTHROW *je_memalign(size_t alignment, size_t size) + void JEMALLOC_SYS_NOTHROW *je_memalign(size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc); #endif #ifdef JEMALLOC_OVERRIDE_VALLOC JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN - void JEMALLOC_NOTHROW *je_valloc(size_t size) JEMALLOC_CXX_THROW + void JEMALLOC_SYS_NOTHROW *je_valloc(size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc); #endif @@ -380,11 +413,12 @@ struct extent_hooks_s { # define mallctlnametomib je_mallctlnametomib # define malloc je_malloc # define malloc_conf je_malloc_conf +# define malloc_conf_2_conf_harder je_malloc_conf_2_conf_harder # define malloc_message je_malloc_message # define malloc_stats_print je_malloc_stats_print # define malloc_usable_size je_malloc_usable_size # define mallocx je_mallocx -# define smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 +# define smallocx_54eaed1d8b56b1aa528be3bdd1877e59c56fa90c je_smallocx_54eaed1d8b56b1aa528be3bdd1877e59c56fa90c # define nallocx je_nallocx # define posix_memalign je_posix_memalign # define rallocx je_rallocx @@ -412,11 +446,12 @@ struct extent_hooks_s { # undef je_mallctlnametomib # undef je_malloc # undef je_malloc_conf +# undef je_malloc_conf_2_conf_harder # undef je_malloc_message # undef je_malloc_stats_print # undef je_malloc_usable_size # undef je_mallocx -# undef je_smallocx_ea6b3e973b477b8061e0076bb257dbd7f3faa756 +# undef je_smallocx_54eaed1d8b56b1aa528be3bdd1877e59c56fa90c # undef je_nallocx # undef je_posix_memalign # undef je_rallocx diff --git a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_FreeBSD.h b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_FreeBSD.h index e733906bc146..34c86271ab2e 100644 --- a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_FreeBSD.h +++ b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_FreeBSD.h @@ -2,6 +2,8 @@ * Override settings that were generated in jemalloc_defs.h as necessary. */ +#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE + #undef JEMALLOC_OVERRIDE_VALLOC #ifndef MALLOC_PRODUCTION @@ -96,8 +98,12 @@ #include <machine/cpufunc.h> #define CPU_SPINWAIT cpu_spinwait() -/* Disable lazy-lock machinery, mangle isthreaded, and adjust its type. */ +/* + * Disable lazy-lock machinery, redirect isthreaded to libc's flag, undo + * jemalloc's namespace stuff for it and adjust its type. + */ #undef JEMALLOC_LAZY_LOCK +#undef isthreaded extern int __isthreaded; #define isthreaded ((bool)__isthreaded) @@ -197,3 +203,5 @@ __sym_compat(sallocm, weak_sallocm, FBSD_1.3); __sym_compat(dallocm, weak_dallocm, FBSD_1.3); __sym_compat(nallocm, weak_nallocm, FBSD_1.3); #endif + +#endif diff --git a/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_defs.h b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_defs.h new file mode 100644 index 000000000000..fb104aaeec98 --- /dev/null +++ b/lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_defs.h @@ -0,0 +1,55 @@ +/* include/jemalloc/jemalloc_defs.h. Generated from jemalloc_defs.h.in by configure. */ +/* Defined if __attribute__((...)) syntax is supported. */ +#define JEMALLOC_HAVE_ATTR + +/* Defined if alloc_size attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_ALLOC_SIZE + +/* Defined if format_arg(...) attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_FORMAT_ARG + +/* Defined if format(gnu_printf, ...) attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF + +/* Defined if format(printf, ...) attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_FORMAT_PRINTF + +/* Defined if fallthrough attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_FALLTHROUGH + +/* Defined if cold attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_COLD + +/* + * Define overrides for non-standard allocator-related functions if they are + * present on the system. + */ +#define JEMALLOC_OVERRIDE_MEMALIGN +#define JEMALLOC_OVERRIDE_VALLOC + +/* + * At least Linux omits the "const" in: + * + * size_t malloc_usable_size(const void *ptr); + * + * Match the operating system's prototype. + */ +#define JEMALLOC_USABLE_SIZE_CONST const + +/* + * If defined, specify throw() for the public function prototypes when compiling + * with C++. The only justification for this is to match the prototypes that + * glibc defines. + */ +/* #undef JEMALLOC_USE_CXX_THROW */ + +#ifdef _MSC_VER +# ifdef _WIN64 +# define LG_SIZEOF_PTR_WIN 3 +# else +# define LG_SIZEOF_PTR_WIN 2 +# endif +#endif + +/* sizeof(void *) == 2^LG_SIZEOF_PTR. */ +#define LG_SIZEOF_PTR 3 diff --git a/lib/libc/stdlib/qsort.c b/lib/libc/stdlib/qsort.c index e0b06494cf98..400124593d07 100644 --- a/lib/libc/stdlib/qsort.c +++ b/lib/libc/stdlib/qsort.c @@ -106,13 +106,11 @@ local_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) char *pa, *pb, *pc, *pd, *pl, *pm, *pn; size_t d1, d2; int cmp_result; - int swap_cnt; /* if there are less than 2 elements, then sorting is not needed */ if (__predict_false(n < 2)) return; loop: - swap_cnt = 0; if (n < 7) { for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; @@ -141,7 +139,6 @@ loop: for (;;) { while (pb <= pc && (cmp_result = CMP(thunk, pb, a)) <= 0) { if (cmp_result == 0) { - swap_cnt = 1; swapfunc(pa, pb, es); pa += es; } @@ -149,7 +146,6 @@ loop: } while (pb <= pc && (cmp_result = CMP(thunk, pc, a)) >= 0) { if (cmp_result == 0) { - swap_cnt = 1; swapfunc(pc, pd, es); pd -= es; } @@ -158,18 +154,9 @@ loop: if (pb > pc) break; swapfunc(pb, pc, es); - swap_cnt = 1; pb += es; pc -= es; } - if (swap_cnt == 0) { /* Switch to insertion sort */ - for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) - for (pl = pm; - pl > (char *)a && CMP(thunk, pl - es, pl) > 0; - pl -= es) - swapfunc(pl, pl - es, es); - return; - } pn = (char *)a + n * es; d1 = MIN(pa - (char *)a, pb - pa); diff --git a/lib/libc/stdlib/reallocarray.3 b/lib/libc/stdlib/reallocarray.3 index 80035c67a497..9a2ab5c7a840 100644 --- a/lib/libc/stdlib/reallocarray.3 +++ b/lib/libc/stdlib/reallocarray.3 @@ -26,7 +26,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 1, 2015 +.Dd October 2, 2025 .Dt REALLOCARRAY 3 .Os .Sh NAME @@ -38,6 +38,8 @@ .In stdlib.h .Ft void * .Fn reallocarray "void *ptr" "size_t nmemb" "size_t size" +.Ft void * +.Fn recallocarray "void *ptr" "size_t oldnmeb" "size_t nmemb" size_t size" .Sh DESCRIPTION The .Fn reallocarray @@ -52,6 +54,33 @@ and checks for integer overflow in the calculation .Fa nmemb * .Fa size . +.Pp +The +.Fn recallocarray +function is similar to the +.Fn reallocarray +function +except it ensures newly allocated memory is cleared similar to +.Fn calloc . +If +.Fa ptr +is +.Dv NULL , +.Fa oldnmemb +is ignored and the call is equivalent to +.Fn calloc . +If +.Fa ptr +is not +.Dv NULL , +.Fa oldnmemb +must be a value such that +.Fa oldnmemb +* +.Fa size +is the size of the earlier allocation that returned +.Fa ptr , +otherwise the behaviour is undefined. .Sh RETURN VALUES The .Fn reallocarray @@ -142,3 +171,9 @@ function first appeared in .Ox 5.6 and .Fx 11.0 . +The +.Fn recallocarray +function first appeared in +.Ox 6.1 +and +.Fx 16.0 . diff --git a/lib/libc/stdlib/reallocarray.c b/lib/libc/stdlib/reallocarray.c index 0868804486cc..3632734c84de 100644 --- a/lib/libc/stdlib/reallocarray.c +++ b/lib/libc/stdlib/reallocarray.c @@ -17,23 +17,19 @@ #include <sys/types.h> #include <errno.h> +#include <stdckdint.h> #include <stdint.h> #include <stdlib.h> -/* - * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX - * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW - */ -#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) - void * reallocarray(void *optr, size_t nmemb, size_t size) { + size_t nbytes; - if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && - nmemb > 0 && SIZE_MAX / nmemb < size) { + if (ckd_mul(&nbytes, nmemb, size)) { errno = ENOMEM; return (NULL); } - return (realloc(optr, size * nmemb)); + + return (realloc(optr, nbytes)); } diff --git a/lib/libc/stdlib/realpath.3 b/lib/libc/stdlib/realpath.3 index 065ba312c2ef..76f40249963b 100644 --- a/lib/libc/stdlib/realpath.3 +++ b/lib/libc/stdlib/realpath.3 @@ -28,7 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 11, 2012 +.Dd October 10, 2025 .Dt REALPATH 3 .Os .Sh NAME @@ -108,11 +108,11 @@ and .Xr getcwd 3 . .Sh SEE ALSO .Xr getcwd 3 -.\" .Sh STANDARDS -.\" The -.\" .Fn realpath -.\" function conforms to -.\" .St -p1003.1-2001 . +.Sh STANDARDS +The +.Fn realpath +function conforms to +.St -p1003.1-2001 . .Sh HISTORY The .Fn realpath diff --git a/lib/libc/stdlib/realpath.c b/lib/libc/stdlib/realpath.c index 4c52b73319ab..18f29e95ee6b 100644 --- a/lib/libc/stdlib/realpath.c +++ b/lib/libc/stdlib/realpath.c @@ -49,7 +49,7 @@ realpath1(const char *path, char *resolved) { struct stat sb; char *p, *q; - size_t left_len, resolved_len, next_token_len; + size_t left_len, prev_len, resolved_len, next_token_len; unsigned symlinks; ssize_t slen; char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; @@ -98,6 +98,7 @@ realpath1(const char *path, char *resolved) left_len = 0; } + prev_len = resolved_len; if (resolved[resolved_len - 1] != '/') { if (resolved_len + 1 >= PATH_MAX) { errno = ENAMETOOLONG; @@ -133,8 +134,17 @@ realpath1(const char *path, char *resolved) errno = ENAMETOOLONG; return (NULL); } - if (lstat(resolved, &sb) != 0) + if (lstat(resolved, &sb) != 0) { + /* + * EACCES means the parent directory is not + * readable, while ENOTDIR means the parent + * directory is not a directory. Rewind the path + * to correctly indicate where the error lies. + */ + if (errno == EACCES || errno == ENOTDIR) + resolved[prev_len] = '\0'; return (NULL); + } if (S_ISLNK(sb.st_mode)) { if (symlinks++ > MAXSYMLINKS) { errno = ELOOP; diff --git a/lib/libc/stdlib/recallocarray.c b/lib/libc/stdlib/recallocarray.c new file mode 100644 index 000000000000..cbf1fb2470cf --- /dev/null +++ b/lib/libc/stdlib/recallocarray.c @@ -0,0 +1,73 @@ +/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */ +/* + * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <stdckdint.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +void *recallocarray(void *, size_t, size_t, size_t); + +void * +recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) +{ + size_t oldsize, newsize; + void *newptr; + + if (ptr == NULL) + return calloc(newnmemb, size); + + if (ckd_mul(&newsize, newnmemb, size)) { + errno = ENOMEM; + return NULL; + } + + if (ckd_mul(&oldsize, oldnmemb, size)) { + errno = EINVAL; + return NULL; + } + + /* + * Don't bother too much if we're shrinking just a bit, + * we do not shrink for series of small steps, oh well. + */ + if (newsize <= oldsize) { + size_t d = oldsize - newsize; + + if (d < oldsize / 2 && d < (size_t)getpagesize()) { + memset((char *)ptr + newsize, 0, d); + return ptr; + } + } + + newptr = malloc(newsize); + if (newptr == NULL) + return NULL; + + if (newsize > oldsize) { + memcpy(newptr, ptr, oldsize); + memset((char *)newptr + oldsize, 0, newsize - oldsize); + } else + memcpy(newptr, ptr, newsize); + + explicit_bzero(ptr, oldsize); + free(ptr); + + return newptr; +} diff --git a/lib/libc/stdtime/Makefile.inc b/lib/libc/stdtime/Makefile.inc index 5d0ce7765b63..1baa39a6c0a6 100644 --- a/lib/libc/stdtime/Makefile.inc +++ b/lib/libc/stdtime/Makefile.inc @@ -18,6 +18,7 @@ CFLAGS.${src}+= -I${LIBC_SRCTOP}/stdtime CFLAGS.localtime.c+= -DALL_STATE -DTHREAD_SAFE .if ${MK_DETECT_TZ_CHANGES} != "no" CFLAGS.localtime.c+= -DDETECT_TZ_CHANGES +CFLAGS.Version.map+= -DDETECT_TZ_CHANGES .endif MAN+= ctime.3 strftime.3 strptime.3 time2posix.3 tzset.3 @@ -31,4 +32,5 @@ MLINKS+=strftime.3 strftime_l.3 MLINKS+=strptime.3 strptime_l.3 MLINKS+=time2posix.3 posix2time.3 MLINKS+=tzset.3 daylight.3 \ - tzset.3 timezone.3 + tzset.3 timezone.3 \ + tzset.3 tzname.3 diff --git a/lib/libc/stdtime/Symbol.map b/lib/libc/stdtime/Symbol.map index 669d4d47907b..6a34cd3ea590 100644 --- a/lib/libc/stdtime/Symbol.map +++ b/lib/libc/stdtime/Symbol.map @@ -35,3 +35,9 @@ FBSD_1.8 { daylight; timezone; }; + +FBSDprivate_1.0 { +#ifdef DETECT_TZ_CHANGES + __tz_change_interval; +#endif +}; diff --git a/lib/libc/stdtime/ctime.3 b/lib/libc/stdtime/ctime.3 index 96b7f775535a..6384e8bd959b 100644 --- a/lib/libc/stdtime/ctime.3 +++ b/lib/libc/stdtime/ctime.3 @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 26, 2024 +.Dd September 23, 2025 .Dt CTIME 3 .Os .Sh NAME @@ -41,6 +41,8 @@ .Nm localtime , .Nm localtime_r , .Nm mktime , +.Nm offtime , +.Nm offtime_r , .Nm timegm .Nd transform binary date and time values .Sh LIBRARY @@ -68,14 +70,19 @@ .Fn localtime_r "const time_t *clock" "struct tm *result" .Ft time_t .Fn mktime "struct tm *tm" +.Ft struct tm * +.Fn offtime "const time_t *clock" "long offset" +.Ft struct tm * +.Fn offtime_r "const time_t *clock" "long offset" "struct tm *result" .Ft time_t .Fn timegm "struct tm *tm" .Sh DESCRIPTION The .Fn ctime , .Fn gmtime , +.Fn localtime , and -.Fn localtime +.Fn offtime functions all take as argument a pointer to a time value representing the time in seconds since the Epoch (00:00:00 UTC on January 1, 1970; see @@ -123,6 +130,18 @@ adjustment, and returns a pointer to a .Vt struct tm . .Pp The +.Fn offtime +function similarly converts the time value with a time zone adjustment +corresponding to the provided +.Fa offset , +which is expressed in seconds, with positive values indicating a time +zone ahead of UTC (east of the Prime Meridian). +It does not call +.Xr tzset 3 +or modify +.Va tzname . +.Pp +The .Fn ctime function adjusts the time value for the current time zone in the same manner as @@ -155,13 +174,15 @@ except the caller must provide the output buffer .Fa buf , which must be at least 26 characters long, to store the result in. The -.Fn localtime_r +.Fn localtime_r , +.Fn gmtime_r , and -.Fn gmtime_r +.Fn offtime_r functions provide the same functionality as -.Fn localtime +.Fn localtime , +.Fn gmtime , and -.Fn gmtime +.Fn offtime respectively, except the caller must provide the output buffer .Fa result . .Pp @@ -368,6 +389,12 @@ and .Fn localtime_r functions have been available since .Fx 8.0 . +The +.Fn offtime +and +.Fn offtime_r +functions were added in +.Fx 15.0 . .Sh BUGS Except for .Fn difftime , diff --git a/lib/libc/stdtime/strftime.c b/lib/libc/stdtime/strftime.c index 5943e6387174..89c9e4cf88ff 100644 --- a/lib/libc/stdtime/strftime.c +++ b/lib/libc/stdtime/strftime.c @@ -1,4 +1,6 @@ /* + * SPDX-License-Identifier: BSD-4.3TAHOE + * * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. * diff --git a/lib/libc/stdtime/strptime.3 b/lib/libc/stdtime/strptime.3 index 7df73d2d080a..9456fa757b85 100644 --- a/lib/libc/stdtime/strptime.3 +++ b/lib/libc/stdtime/strptime.3 @@ -171,7 +171,7 @@ is taken as noon. The .Fa %Z format specifier only accepts time zone abbreviations of the local time zone, -or the value "GMT". +and the values "GMT", "UTC", or "Z". This limitation is because of ambiguity due to of the over loading of time zone abbreviations. One such example is diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c index 5f1293c7a267..375e49146639 100644 --- a/lib/libc/stdtime/strptime.c +++ b/lib/libc/stdtime/strptime.c @@ -546,7 +546,8 @@ label: zonestr[cp - buf] = '\0'; tzset(); if (0 == strcmp(zonestr, "GMT") || - 0 == strcmp(zonestr, "UTC")) { + 0 == strcmp(zonestr, "UTC") || + 0 == strcmp(zonestr, "Z")) { *GMTp = 1; } else if (0 == strcmp(zonestr, tzname[0])) { tm->tm_isdst = 0; diff --git a/lib/libc/stdtime/tzset.3 b/lib/libc/stdtime/tzset.3 index 94ccbec9aba7..33e6a556306d 100644 --- a/lib/libc/stdtime/tzset.3 +++ b/lib/libc/stdtime/tzset.3 @@ -28,7 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 25, 2023 +.Dd September 30, 2025 .Dt TZSET 3 .Os .Sh NAME @@ -58,8 +58,8 @@ specifies how this is done. .Pp If .Ev TZ -does not appear in the environment, the best available approximation to -local wall clock time, as specified by the +does not appear in the environment, the best available approximation +to local wall clock time, as specified by the .Xr tzfile 5 Ns -format file .Pa /etc/localtime @@ -68,9 +68,7 @@ is used. If .Ev TZ appears in the environment but its value is a null string, Coordinated -Universal Time -.Pq Tn UTC -is used (without leap second correction). +Universal Time (UTC) is used (without leap second correction). .Pp If .Ev TZ @@ -81,13 +79,12 @@ the rest of its value is used as a pathname of a file from which to read the time conversion information. If the first character of the pathname is a slash .Pq Ql / -it is used as -an absolute pathname; otherwise, it is used as a pathname relative to -the system time conversion information directory. +it is used as an absolute pathname; otherwise, it is used as a +pathname relative to the system time conversion information directory. .Pp -If its value does not begin with a colon, it is first used as the pathname -of a file (as described above) from which to read the time conversion -information. +If its value does not begin with a colon, it is first used as the +pathname of a file (as described above) from which to read the time +conversion information. If that file cannot be read, the value is then interpreted as a direct specification (the format is described below) of the time conversion information. @@ -96,24 +93,23 @@ If the .Ev TZ environment variable does not specify a .Xr tzfile 5 Ns -format -file and cannot be interpreted as a direct specification, -.Tn UTC -is used. +file and cannot be interpreted as a direct specification, UTC is used. .Pp After the first call to .Nm tzset , the .Vt timezone -variable is set to the difference, in seconds, between Coordinated Universal -Time (UTC) and the local time. +variable is set to the difference, in seconds, between Coordinated +Universal Time (UTC) and the local time. The .Vt daylight variable is set to 0 if the local timezone is observing standard time. -It is set to 1 if the local timezone ever observes an alternate time, such as summer time. +It is set to 1 if the local timezone ever observes an alternate time, +such as summer time. The first element of the .Vt tzname -array is the timezone name of standard time, while the second element is the -name of the altnerative time zone. +array is the timezone name of standard time, while the second element +is the name of the altnerative time zone. .Sh SPECIFICATION FORMAT When .Ev TZ @@ -151,8 +147,7 @@ minus .Pq Ql \- , plus .Pq Ql + , -and -.Tn ASCII +and ASCII .Dv NUL are allowed. .It Em offset @@ -294,10 +289,8 @@ by the file .Em posixrules in the system time conversion information directory are used, with the -standard and summer time offsets from -.Tn UTC -replaced by those specified by -the +standard and summer time offsets from UTC replaced by those specified +by the .Em offset values in .Ev TZ . @@ -315,20 +308,14 @@ local time zone file .It Pa /usr/share/zoneinfo time zone directory .It Pa /usr/share/zoneinfo/posixrules -rules for -.Tn POSIX Ns -style -.Tn TZ Ns 's +rules for POSIX-style TZs .It Pa /usr/share/zoneinfo/Etc/GMT -for -.Tn UTC -leap seconds +for UTC leap seconds .El .Pp If the file .Pa /usr/share/zoneinfo/UTC -does not exist, -.Tn UTC -leap seconds are loaded from +does not exist, UTC leap seconds are loaded from .Pa /usr/share/zoneinfo/posixrules . .Sh SEE ALSO .Xr date 1 , diff --git a/lib/libc/string/memccpy.3 b/lib/libc/string/memccpy.3 index 2de981449119..0d35a68fce67 100644 --- a/lib/libc/string/memccpy.3 +++ b/lib/libc/string/memccpy.3 @@ -80,7 +80,7 @@ The function conforms to .St -p1003.1-2004 and -.St -isoC-2024 . +.St -isoC-2023 . .Sh HISTORY The .Fn memccpy diff --git a/lib/libc/string/memchr.3 b/lib/libc/string/memchr.3 index f5d1fe5d5c7f..c50e932d3382 100644 --- a/lib/libc/string/memchr.3 +++ b/lib/libc/string/memchr.3 @@ -34,7 +34,7 @@ .Os .Sh NAME .Nm memchr -.Nd locate byte in byte string +.Nd locate byte in memory object .Sh LIBRARY .Lb libc .Sh SYNOPSIS @@ -51,8 +51,11 @@ locates the first occurrence of .Fa c (converted to an .Vt "unsigned char" ) -in string -.Fa b . +in object +.Fa b , +limited to at most +.Fa len +characters. .Pp The .Fn memrchr @@ -60,16 +63,19 @@ function behaves like .Fn memchr , except that it locates the last occurrence of .Fa c -in string -.Fa b . +in object +.Fa b , +limited to the first +.Fa len +characters. .Sh RETURN VALUES The .Fn memchr and .Fn memrchr -functions -return a pointer to the byte located, -or NULL if no such byte exists within +functions return a pointer to the byte located, or +.Dv NULL +if no such byte exists within .Fa len bytes. .Sh SEE ALSO diff --git a/lib/libc/string/memset.3 b/lib/libc/string/memset.3 index f2dba3ec5a48..f6ab9dacb516 100644 --- a/lib/libc/string/memset.3 +++ b/lib/libc/string/memset.3 @@ -141,4 +141,4 @@ conforms to K.3.7.4.1. .Fn memset_explicit conforms to -.St -isoC-2024 . +.St -isoC-2023 . diff --git a/lib/libc/sys/accept.c b/lib/libc/sys/accept.c index 95c8b4a58c74..c6fb040ab6d6 100644 --- a/lib/libc/sys/accept.c +++ b/lib/libc/sys/accept.c @@ -34,8 +34,6 @@ #include <sys/socket.h> #include "libc_private.h" -__weak_reference(__sys_accept, __accept); - #pragma weak accept int accept(int s, struct sockaddr *addr, socklen_t *addrlen) diff --git a/lib/libc/sys/accept4.c b/lib/libc/sys/accept4.c index 1a1f75c9f528..146dde9ae6fa 100644 --- a/lib/libc/sys/accept4.c +++ b/lib/libc/sys/accept4.c @@ -34,8 +34,6 @@ #include <sys/socket.h> #include "libc_private.h" -__weak_reference(__sys_accept4, __accept4); - #pragma weak accept4 int accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags) diff --git a/lib/libc/sys/aio_suspend.c b/lib/libc/sys/aio_suspend.c index 592cb5b0e1d7..5a7774705ac2 100644 --- a/lib/libc/sys/aio_suspend.c +++ b/lib/libc/sys/aio_suspend.c @@ -33,8 +33,6 @@ #include <sys/aio.h> #include "libc_private.h" -__weak_reference(__sys_aio_suspend, __aio_suspend); - #pragma weak aio_suspend int aio_suspend(const struct aiocb * const iocbs[], int niocb, diff --git a/lib/libc/sys/clock_nanosleep.c b/lib/libc/sys/clock_nanosleep.c index a50af30e1fb3..db14bd46f85e 100644 --- a/lib/libc/sys/clock_nanosleep.c +++ b/lib/libc/sys/clock_nanosleep.c @@ -34,8 +34,6 @@ #include <time.h> #include "libc_private.h" -__weak_reference(__sys_clock_nanosleep, __clock_nanosleep); - #pragma weak clock_nanosleep int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, diff --git a/lib/libc/sys/close.c b/lib/libc/sys/close.c index 08a5419361a9..8aa2b9b6ad13 100644 --- a/lib/libc/sys/close.c +++ b/lib/libc/sys/close.c @@ -34,8 +34,6 @@ #include <unistd.h> #include "libc_private.h" -__weak_reference(__sys_close, __close); - #pragma weak close int close(int fd) diff --git a/lib/libc/sys/closefrom.c b/lib/libc/sys/closefrom.c index 292702fad38c..76ed4e744397 100644 --- a/lib/libc/sys/closefrom.c +++ b/lib/libc/sys/closefrom.c @@ -26,20 +26,12 @@ */ #include <sys/param.h> -#include <sys/syscall.h> #include <unistd.h> #include "libc_private.h" -#define CLOSE_RANGE_OSREL 1300091 - void closefrom(int lowfd) { - - if (__getosreldate() >= CLOSE_RANGE_OSREL) - __sys_close_range(MAX(0, lowfd), ~0U, 0); - else - /* Fallback to closefrom(2) on older kernels. */ - syscall(SYS_freebsd12_closefrom, lowfd); + __sys_close_range(MAX(0, lowfd), ~0U, 0); } diff --git a/lib/libc/sys/connect.c b/lib/libc/sys/connect.c index e64451683abc..f27813b63df0 100644 --- a/lib/libc/sys/connect.c +++ b/lib/libc/sys/connect.c @@ -34,8 +34,6 @@ #include <sys/socket.h> #include "libc_private.h" -__weak_reference(__sys_connect, __connect); - #pragma weak connect int connect(int s, const struct sockaddr *addr, socklen_t addrlen) diff --git a/lib/libc/sys/fork.c b/lib/libc/sys/fork.c index e907c51b5cee..3af81c14ffb3 100644 --- a/lib/libc/sys/fork.c +++ b/lib/libc/sys/fork.c @@ -33,8 +33,6 @@ #include <unistd.h> #include "libc_private.h" -__weak_reference(__sys_fork, __fork); - #pragma weak fork pid_t fork(void) diff --git a/lib/libc/sys/fsync.c b/lib/libc/sys/fsync.c index e542c8cdea48..c58b29ca6a77 100644 --- a/lib/libc/sys/fsync.c +++ b/lib/libc/sys/fsync.c @@ -34,8 +34,6 @@ #include <unistd.h> #include "libc_private.h" -__weak_reference(__sys_fsync, __fsync); - int fsync(int fd) { diff --git a/lib/libc/sys/kevent.c b/lib/libc/sys/kevent.c index f16ac89a36c6..e395e16c67f5 100644 --- a/lib/libc/sys/kevent.c +++ b/lib/libc/sys/kevent.c @@ -34,8 +34,6 @@ #include <sys/time.h> #include "libc_private.h" -__weak_reference(__sys_kevent, __kevent); - #pragma weak kevent int kevent(int kq, const struct kevent *changelist, int nchanges, diff --git a/lib/libc/sys/msync.c b/lib/libc/sys/msync.c index f825db97b947..11dd988300eb 100644 --- a/lib/libc/sys/msync.c +++ b/lib/libc/sys/msync.c @@ -34,8 +34,6 @@ #include <sys/mman.h> #include "libc_private.h" -__weak_reference(__sys_msync, __msync); - #pragma weak msync int msync(void *addr, size_t len, int flags) diff --git a/lib/libc/sys/nanosleep.c b/lib/libc/sys/nanosleep.c index bd0794c3af8e..23c07e3bf5fc 100644 --- a/lib/libc/sys/nanosleep.c +++ b/lib/libc/sys/nanosleep.c @@ -33,8 +33,6 @@ #include <time.h> #include "libc_private.h" -__weak_reference(__sys_nanosleep, __nanosleep); - #pragma weak nanosleep int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) diff --git a/lib/libc/sys/open.c b/lib/libc/sys/open.c index 57c07637d2aa..dd7bedebf141 100644 --- a/lib/libc/sys/open.c +++ b/lib/libc/sys/open.c @@ -34,8 +34,6 @@ #include <stdarg.h> #include "libc_private.h" -__weak_reference(__sys_open, __open); - #pragma weak open int open(const char *path, int flags, ...) diff --git a/lib/libc/sys/openat.c b/lib/libc/sys/openat.c index 1cd8e7d90745..ba937cae3a3e 100644 --- a/lib/libc/sys/openat.c +++ b/lib/libc/sys/openat.c @@ -34,7 +34,6 @@ #include <stdarg.h> #include "libc_private.h" -__weak_reference(__sys_openat, __openat); __sym_compat(openat, __impl_openat, FBSD_1.1); __weak_reference(openat, __impl_openat); __sym_default(openat, openat, FBSD_1.2); diff --git a/lib/libc/sys/poll.c b/lib/libc/sys/poll.c index 5e000f7e728f..8c6a89adb2cb 100644 --- a/lib/libc/sys/poll.c +++ b/lib/libc/sys/poll.c @@ -34,8 +34,6 @@ #include <ssp/ssp.h> #include "libc_private.h" -__weak_reference(__sys_poll, __poll); - int __weak_symbol __ssp_real(poll)(struct pollfd pfd[], nfds_t nfds, int timeout) { diff --git a/lib/libc/sys/ppoll.c b/lib/libc/sys/ppoll.c index 72c0b5d5e421..5ffc35a8cbb0 100644 --- a/lib/libc/sys/ppoll.c +++ b/lib/libc/sys/ppoll.c @@ -34,8 +34,6 @@ #include <ssp/ssp.h> #include "libc_private.h" -__weak_reference(__sys_ppoll, __ppoll); - int __weak_symbol __ssp_real(ppoll)(struct pollfd pfd[], nfds_t nfds, const struct timespec *__restrict timeout, diff --git a/lib/libc/sys/pselect.c b/lib/libc/sys/pselect.c index 38477e39af44..2c1c9896d6b9 100644 --- a/lib/libc/sys/pselect.c +++ b/lib/libc/sys/pselect.c @@ -33,8 +33,6 @@ #include <sys/select.h> #include "libc_private.h" -__weak_reference(__sys_pselect, __pselect); - #pragma weak pselect int pselect(int n, fd_set *rs, fd_set *ws, fd_set *es, const struct timespec *t, diff --git a/lib/libc/sys/read.c b/lib/libc/sys/read.c index b4610e0c38c3..75cc7e42b013 100644 --- a/lib/libc/sys/read.c +++ b/lib/libc/sys/read.c @@ -34,8 +34,6 @@ #include <unistd.h> #include "libc_private.h" -__weak_reference(__sys_read, __read); - #pragma weak read ssize_t read(int fd, void *buf, size_t nbytes) diff --git a/lib/libc/sys/readv.c b/lib/libc/sys/readv.c index 2729f6ed99da..83b4bf9ba3e6 100644 --- a/lib/libc/sys/readv.c +++ b/lib/libc/sys/readv.c @@ -36,8 +36,6 @@ #include <ssp/ssp.h> #include "libc_private.h" -__weak_reference(__sys_readv, __readv); - ssize_t __weak_symbol __ssp_real(readv)(int fd, const struct iovec *iov, int iovcnt) { diff --git a/lib/libc/sys/recvfrom.c b/lib/libc/sys/recvfrom.c index 0cdbd8a4ccaa..f7e2dd2ca6c8 100644 --- a/lib/libc/sys/recvfrom.c +++ b/lib/libc/sys/recvfrom.c @@ -35,8 +35,6 @@ #include <ssp/ssp.h> #include "libc_private.h" -__weak_reference(__sys_recvfrom, __recvfrom); - ssize_t __weak_symbol __ssp_real(recvfrom)(int s, void *buf, size_t len, int flags, struct sockaddr * __restrict from, socklen_t * __restrict fromlen) diff --git a/lib/libc/sys/recvmsg.c b/lib/libc/sys/recvmsg.c index ce0f5e0478cd..7c820c47b325 100644 --- a/lib/libc/sys/recvmsg.c +++ b/lib/libc/sys/recvmsg.c @@ -35,8 +35,6 @@ #include <ssp/ssp.h> #include "libc_private.h" -__weak_reference(__sys_recvmsg, __recvmsg); - ssize_t __weak_symbol __ssp_real(recvmsg)(int s, struct msghdr *msg, int flags) { diff --git a/lib/libc/sys/select.c b/lib/libc/sys/select.c index bbbcfc8bf1e0..909449f17da8 100644 --- a/lib/libc/sys/select.c +++ b/lib/libc/sys/select.c @@ -33,8 +33,6 @@ #include <sys/select.h> #include "libc_private.h" -__weak_reference(__sys_select, __select); - #pragma weak select int select(int n, fd_set *rs, fd_set *ws, fd_set *es, struct timeval *t) diff --git a/lib/libc/sys/sendmsg.c b/lib/libc/sys/sendmsg.c index fd4e41837c48..6c5fb4021ce3 100644 --- a/lib/libc/sys/sendmsg.c +++ b/lib/libc/sys/sendmsg.c @@ -34,8 +34,6 @@ #include <sys/socket.h> #include "libc_private.h" -__weak_reference(__sys_sendmsg, __sendmsg); - #pragma weak sendmsg ssize_t sendmsg(int s, const struct msghdr *msg, int flags) diff --git a/lib/libc/sys/sendto.c b/lib/libc/sys/sendto.c index 708fc6ec9368..b8a173f434a9 100644 --- a/lib/libc/sys/sendto.c +++ b/lib/libc/sys/sendto.c @@ -34,8 +34,6 @@ #include <sys/socket.h> #include "libc_private.h" -__weak_reference(__sys_sendto, __sendto); - #pragma weak sendto ssize_t sendto(int s, const void *msg, size_t len, int flags, diff --git a/lib/libc/sys/setcontext.c b/lib/libc/sys/setcontext.c index 96b5dc115067..a750c4dfea30 100644 --- a/lib/libc/sys/setcontext.c +++ b/lib/libc/sys/setcontext.c @@ -33,7 +33,6 @@ #include <ucontext.h> #include "libc_private.h" -__weak_reference(__sys_setcontext, __setcontext); __sym_compat(setcontext, __impl_setcontext, FBSD_1.0); __weak_reference(setcontext, __impl_setcontext); __sym_default(setcontext, setcontext, FBSD_1.2); diff --git a/lib/libc/sys/sigaction.c b/lib/libc/sys/sigaction.c index 809881ca4d3f..7ce177895c81 100644 --- a/lib/libc/sys/sigaction.c +++ b/lib/libc/sys/sigaction.c @@ -33,7 +33,6 @@ #include <signal.h> #include "libc_private.h" -__weak_reference(__sys_sigaction, __sigaction); __weak_reference(sigaction, __libc_sigaction); #pragma weak sigaction diff --git a/lib/libc/sys/sigprocmask.c b/lib/libc/sys/sigprocmask.c index 96cb9586509b..8b7fb6fb8e74 100644 --- a/lib/libc/sys/sigprocmask.c +++ b/lib/libc/sys/sigprocmask.c @@ -33,7 +33,6 @@ #include <signal.h> #include "libc_private.h" -__weak_reference(__sys_sigprocmask, __sigprocmask); __weak_reference(sigprocmask, __libc_sigprocmask); #pragma weak sigprocmask diff --git a/lib/libc/sys/sigsuspend.c b/lib/libc/sys/sigsuspend.c index e5a9495f9f39..ac117650ed1b 100644 --- a/lib/libc/sys/sigsuspend.c +++ b/lib/libc/sys/sigsuspend.c @@ -33,7 +33,6 @@ #include <signal.h> #include "libc_private.h" -__weak_reference(__sys_sigsuspend, __sigsuspend); __weak_reference(sigsuspend, __libc_sigsuspend); #pragma weak sigsuspend diff --git a/lib/libc/sys/sigtimedwait.c b/lib/libc/sys/sigtimedwait.c index 839e5fe4d992..4efa69555f39 100644 --- a/lib/libc/sys/sigtimedwait.c +++ b/lib/libc/sys/sigtimedwait.c @@ -33,8 +33,6 @@ #include <signal.h> #include "libc_private.h" -__weak_reference(__sys_sigtimedwait, __sigtimedwait); - #pragma weak sigtimedwait int sigtimedwait(const sigset_t * __restrict set, siginfo_t * __restrict info, diff --git a/lib/libc/sys/sigwaitinfo.c b/lib/libc/sys/sigwaitinfo.c index 611d09c3c221..e94777b731e6 100644 --- a/lib/libc/sys/sigwaitinfo.c +++ b/lib/libc/sys/sigwaitinfo.c @@ -33,8 +33,6 @@ #include <signal.h> #include "libc_private.h" -__weak_reference(__sys_sigwaitinfo, __sigwaitinfo); - #pragma weak sigwaitinfo int sigwaitinfo(const sigset_t * __restrict set, siginfo_t * __restrict info) diff --git a/lib/libc/sys/swapcontext.c b/lib/libc/sys/swapcontext.c index b69a5b19e698..69d4dfea16b5 100644 --- a/lib/libc/sys/swapcontext.c +++ b/lib/libc/sys/swapcontext.c @@ -35,7 +35,6 @@ #include <stddef.h> #include "libc_private.h" -__weak_reference(__sys_swapcontext, __swapcontext); __sym_compat(swapcontext, __impl_swapcontext, FBSD_1.0); __weak_reference(swapcontext, __impl_swapcontext); __sym_default(swapcontext, swapcontext, FBSD_1.2); diff --git a/lib/libc/sys/wait4.c b/lib/libc/sys/wait4.c index 57b0071ef87f..1d30c2d44c19 100644 --- a/lib/libc/sys/wait4.c +++ b/lib/libc/sys/wait4.c @@ -33,8 +33,6 @@ #include <sys/wait.h> #include "libc_private.h" -__weak_reference(__sys_wait4, __wait4); - #pragma weak wait4 pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru) diff --git a/lib/libc/sys/wait6.c b/lib/libc/sys/wait6.c index 118a7ef6b99e..70817bdb441c 100644 --- a/lib/libc/sys/wait6.c +++ b/lib/libc/sys/wait6.c @@ -34,8 +34,6 @@ #include <signal.h> #include "libc_private.h" -__weak_reference(__sys_wait6, __wait6); - #pragma weak wait6 pid_t wait6(idtype_t idtype, id_t id, int *status, int options, struct __wrusage *ru, diff --git a/lib/libc/sys/write.c b/lib/libc/sys/write.c index 5880856b8af8..15e2d22fdbc5 100644 --- a/lib/libc/sys/write.c +++ b/lib/libc/sys/write.c @@ -34,8 +34,6 @@ #include <unistd.h> #include "libc_private.h" -__weak_reference(__sys_write, __write); - #pragma weak write ssize_t write(int fd, const void *buf, size_t nbytes) diff --git a/lib/libc/sys/writev.c b/lib/libc/sys/writev.c index 9a32cb603ab8..b642c6af9a89 100644 --- a/lib/libc/sys/writev.c +++ b/lib/libc/sys/writev.c @@ -35,8 +35,6 @@ #include <unistd.h> #include "libc_private.h" -__weak_reference(__sys_writev, __writev); - #pragma weak writev ssize_t writev(int fd, const struct iovec *iov, int iovcnt) diff --git a/lib/libc/tests/db/Makefile b/lib/libc/tests/db/Makefile index f1f33bd2bafc..771569183584 100644 --- a/lib/libc/tests/db/Makefile +++ b/lib/libc/tests/db/Makefile @@ -7,6 +7,10 @@ PROGS+= h_lfsr ${PACKAGE}FILES+= README +ATF_TESTS_C+= dbm_open_test +ATF_TESTS_C+= dbm_perm_test +ATF_TESTS_C+= dbm_nextkey_test + NETBSD_ATF_TESTS_C+= db_hash_seq_test NETBSD_ATF_TESTS_SH+= db_test ATF_TESTS_SH_SED_db_test= -e 's,/bin/csh,/bin/cat,g' diff --git a/lib/libc/tests/db/dbm_nextkey_test.c b/lib/libc/tests/db/dbm_nextkey_test.c new file mode 100644 index 000000000000..67b745efb196 --- /dev/null +++ b/lib/libc/tests/db/dbm_nextkey_test.c @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <fcntl.h> +#include <ndbm.h> +#include <stdio.h> + +#include <atf-c.h> + +static const char *path = "tmp"; +static const char *dbname = "tmp.db"; + +ATF_TC(dbm_nextkey_test); +ATF_TC_HEAD(dbm_nextkey_test, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Check that dbm_nextkey always returns NULL after reaching the end of the database"); +} + +ATF_TC_BODY(dbm_nextkey_test, tc) +{ + DBM *db; + datum key, data; + + data.dptr = "bar"; + data.dsize = strlen("bar"); + key.dptr = "foo"; + key.dsize = strlen("foo"); + + db = dbm_open(path, O_RDWR | O_CREAT, 0755); + ATF_CHECK(db != NULL); + ATF_REQUIRE(atf_utils_file_exists(dbname)); + ATF_REQUIRE(dbm_store(db, key, data, DBM_INSERT) != -1); + + key = dbm_firstkey(db); + ATF_REQUIRE(key.dptr != NULL); + key = dbm_nextkey(db); + ATF_REQUIRE(key.dptr == NULL); + key = dbm_nextkey(db); + ATF_REQUIRE(key.dptr == NULL); + + dbm_close(db); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, dbm_nextkey_test); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/db/dbm_open_test.c b/lib/libc/tests/db/dbm_open_test.c new file mode 100644 index 000000000000..8a3e888bf72c --- /dev/null +++ b/lib/libc/tests/db/dbm_open_test.c @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <fcntl.h> +#include <ndbm.h> +#include <stdio.h> + +#include <atf-c.h> + +static const char *path = "tmp"; +static const char *dbname = "tmp.db"; + +ATF_TC(dbm_open_missing_test); +ATF_TC_HEAD(dbm_open_missing_test, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test dbm_open when creating a new database"); +} + +ATF_TC_BODY(dbm_open_missing_test, tc) +{ + + /* + * POSIX.1 specifies that a missing database file should + * always get created if O_CREAT is present, except when + * O_EXCL is specified as well. + */ + ATF_CHECK(dbm_open(path, O_RDONLY, 0755) == NULL); + ATF_REQUIRE(!atf_utils_file_exists(dbname)); + ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT, 0755) != NULL); + ATF_REQUIRE(atf_utils_file_exists(dbname)); + ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT | O_EXCL, 0755) == NULL); +} + +ATF_TC_WITHOUT_HEAD(dbm_open_wronly_test); +ATF_TC_BODY(dbm_open_wronly_test, tc) +{ + ATF_CHECK(dbm_open(path, O_WRONLY, 0755) == NULL); + ATF_REQUIRE(!atf_utils_file_exists(dbname)); + ATF_CHECK(dbm_open(path, O_WRONLY | O_CREAT, 0755) != NULL); + ATF_REQUIRE(atf_utils_file_exists(dbname)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, dbm_open_missing_test); + ATF_TP_ADD_TC(tp, dbm_open_wronly_test); + return (atf_no_error()); +} diff --git a/lib/libc/tests/db/dbm_perm_test.c b/lib/libc/tests/db/dbm_perm_test.c new file mode 100644 index 000000000000..c07210292014 --- /dev/null +++ b/lib/libc/tests/db/dbm_perm_test.c @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <errno.h> +#include <fcntl.h> +#include <ndbm.h> +#include <stdio.h> + +#include <atf-c.h> + +static const char *path = "tmp"; +static const char *dbname = "tmp.db"; + +static void +create_db(void) +{ + DB *db; + datum data, key; + + data.dptr = "bar"; + data.dsize = strlen("bar"); + key.dptr = "foo"; + key.dsize = strlen("foo"); + + db = dbm_open(path, O_RDWR | O_CREAT, 0755); + ATF_CHECK(db != NULL); + ATF_REQUIRE(atf_utils_file_exists(dbname)); + ATF_REQUIRE(dbm_store(db, key, data, DBM_INSERT) != -1); + dbm_close(db); +} + +ATF_TC_WITHOUT_HEAD(dbm_rdonly_test); +ATF_TC_BODY(dbm_rdonly_test, tc) +{ + DB *db; + datum data, key; + + bzero(&data, sizeof(data)); + key.dptr = "foo"; + key.dsize = strlen("foo"); + create_db(); + + db = dbm_open(path, O_RDONLY, 0755); + data = dbm_fetch(db, key); + ATF_REQUIRE(data.dptr != NULL); + ATF_REQUIRE(strncmp((const char*)data.dptr, "bar", data.dsize) == 0); + ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) == -1); + ATF_REQUIRE(errno == EPERM); +} + +ATF_TC_WITHOUT_HEAD(dbm_wronly_test); +ATF_TC_BODY(dbm_wronly_test, tc) +{ + DB *db; + datum data, key; + + key.dptr = "foo"; + key.dsize = strlen("foo"); + data.dptr = "baz"; + data.dsize = strlen("baz"); + create_db(); + + db = dbm_open(path, O_WRONLY, 0755); + data = dbm_fetch(db, key); + ATF_REQUIRE(data.dptr == NULL); + ATF_REQUIRE(errno == EPERM); + ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) != -1); +} + +ATF_TC_WITHOUT_HEAD(dbm_rdwr_test); +ATF_TC_BODY(dbm_rdwr_test, tc) +{ + DB *db; + datum data, key; + + key.dptr = "foo"; + key.dsize = strlen("foo"); + create_db(); + + db = dbm_open(path, O_RDWR, 0755); + data = dbm_fetch(db, key); + ATF_REQUIRE(data.dptr != NULL); + data.dptr = "baz"; + data.dsize = strlen("baz"); + ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) != -1); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, dbm_rdonly_test); + ATF_TP_ADD_TC(tp, dbm_wronly_test); + ATF_TP_ADD_TC(tp, dbm_rdwr_test); + + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile index 87f2b8320d25..8c2151105209 100644 --- a/lib/libc/tests/gen/Makefile +++ b/lib/libc/tests/gen/Makefile @@ -7,14 +7,28 @@ ATF_TESTS_C+= fmtcheck2_test ATF_TESTS_C+= fmtmsg_test ATF_TESTS_C+= fnmatch2_test ATF_TESTS_C+= fpclassify2_test +.if ${COMPILER_FEATURES:Mblocks} +ATF_TESTS_C+= fts_blocks_test +.endif +ATF_TESTS_C+= fts_misc_test +ATF_TESTS_C+= fts_options_test ATF_TESTS_C+= ftw_test ATF_TESTS_C+= getentropy_test ATF_TESTS_C+= getmntinfo_test ATF_TESTS_C+= glob2_test +.if ${COMPILER_FEATURES:Mblocks} +ATF_TESTS_C+= glob_blocks_test +.endif ATF_TESTS_C+= makecontext_test +ATF_TESTS_C+= opendir_test ATF_TESTS_C+= popen_test ATF_TESTS_C+= posix_spawn_test ATF_TESTS_C+= realpath2_test +ATF_TESTS_C+= scandir_test +.if ${COMPILER_FEATURES:Mblocks} +ATF_TESTS_C+= scandir_blocks_test +.endif +ATF_TESTS_C+= sig2str_test ATF_TESTS_C+= sigsetops_test ATF_TESTS_C+= wordexp_test @@ -92,6 +106,20 @@ TEST_METADATA.setdomainname_test+= is_exclusive=true TESTS_SUBDIRS= execve TESTS_SUBDIRS+= posix_spawn +# Tests that require address sanitizer +.if ${COMPILER_FEATURES:Masan} +.for t in scandir_test realpath2_test +CFLAGS.${t}.c+= -fsanitize=address +LDFLAGS.${t}+= -fsanitize=address +.endfor +.endif + +# Tests that require blocks support +.for t in fts_blocks_test glob_blocks_test scandir_blocks_test +CFLAGS.${t}.c+= -fblocks +LIBADD.${t}+= BlocksRuntime +.endfor + # The old testcase name TEST_FNMATCH= test-fnmatch CLEANFILES+= ${GEN_SH_CASE_TESTCASES} diff --git a/lib/libc/tests/gen/fnmatch_test.c b/lib/libc/tests/gen/fnmatch_test.c index 6cdf5f2a05fa..0ff7400a4a4f 100644 --- a/lib/libc/tests/gen/fnmatch_test.c +++ b/lib/libc/tests/gen/fnmatch_test.c @@ -26,6 +26,7 @@ #include <sys/param.h> #include <errno.h> +#include <locale.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -176,10 +177,90 @@ ATF_TC_BODY(fnmatch_test, tc) } +ATF_TC(fnmatch_characterclass); +ATF_TC_HEAD(fnmatch_characterclass, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fnmatch with character classes"); +} + +ATF_TC_BODY(fnmatch_characterclass, tc) +{ + ATF_CHECK(fnmatch("[[:alnum:]]", "a", 0) == 0); + ATF_CHECK(fnmatch("[[:cntrl:]]", "\a", 0) == 0); + ATF_CHECK(fnmatch("[[:lower:]]", "a", 0) == 0); + ATF_CHECK(fnmatch("[[:space:]]", " ", 0) == 0); + ATF_CHECK(fnmatch("[[:alpha:]]", "a", 0) == 0); + ATF_CHECK(fnmatch("[[:digit:]]", "0", 0) == 0); + ATF_CHECK(fnmatch("[[:print:]]", "a", 0) == 0); + ATF_CHECK(fnmatch("[[:upper:]]", "A", 0) == 0); + ATF_CHECK(fnmatch("[[:blank:]]", " ", 0) == 0); + ATF_CHECK(fnmatch("[[:graph:]]", "a", 0) == 0); + ATF_CHECK(fnmatch("[[:punct:]]", ".", 0) == 0); + ATF_CHECK(fnmatch("[[:xdigit:]]", "f", 0) == 0); + + /* + * POSIX.1, section 9.3.5. states that '[:' and ':]' + * should be interpreted as character classes symbol only + * when part of a bracket expression. + */ + ATF_CHECK(fnmatch("[:alnum:]", "a", 0) == 0); + ATF_CHECK(fnmatch("[:alnum:]", ":", 0) == 0); + ATF_CHECK(fnmatch("[:alnum:]", "1", 0) != 0); +} + +ATF_TC(fnmatch_collsym); +ATF_TC_HEAD(fnmatch_collsym, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fnmatch with collating symbols"); +} + +ATF_TC_BODY(fnmatch_collsym, tc) +{ + setlocale(LC_ALL, "cs_CZ.UTF-8"); + ATF_CHECK(fnmatch("[ch]", "ch", 0) != 0); + ATF_CHECK(fnmatch("[[.ch.]]", "ch", 0) == 0); + ATF_CHECK(fnmatch("[[.ch.]]h", "chh", 0) == 0); + + /* + * POSIX.1, section 9.3.5. states that '[.' and '.]' + * should be interpreted as a collating symbol only + * when part of a bracket expression. + */ + ATF_CHECK(fnmatch("[.ch.]", "c", 0) == 0); + ATF_CHECK(fnmatch("[.ch.]", "h", 0) == 0); + ATF_CHECK(fnmatch("[.ch.]", ".", 0) == 0); +} + +ATF_TC(fnmatch_equivclass); +ATF_TC_HEAD(fnmatch_equivclass, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fnmatch with equivalence classes"); +} + +ATF_TC_BODY(fnmatch_equivclass, tc) +{ + setlocale(LC_ALL, "en_US.UTF-8"); + ATF_CHECK(fnmatch("[[=a=]]b", "ab", 0) == 0); + ATF_CHECK(fnmatch("[[=a=]]b", "Ab", 0) == 0); + ATF_CHECK(fnmatch("[[=à=]]b", "ab", 0) == 0); + ATF_CHECK(fnmatch("[[=a=]]b", "àb", 0) == 0); + + /* + * POSIX.1, section 9.3.5. states that '[=' and '=]' + * should be interpreted as an equivalence class only + * when part of a bracket expression. + */ + ATF_CHECK(fnmatch("[=a=]b", "=b", 0) == 0); + ATF_CHECK(fnmatch("[=a=]b", "ab", 0) == 0); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, fnmatch_test); + ATF_TP_ADD_TC(tp, fnmatch_collsym); + ATF_TP_ADD_TC(tp, fnmatch_characterclass); + ATF_TP_ADD_TC(tp, fnmatch_equivclass); return (atf_no_error()); } diff --git a/lib/libc/tests/gen/fts_blocks_test.c b/lib/libc/tests/gen/fts_blocks_test.c new file mode 100644 index 000000000000..f020dd8dea45 --- /dev/null +++ b/lib/libc/tests/gen/fts_blocks_test.c @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <fcntl.h> +#include <fts.h> + +#include <atf-c.h> + +/* + * Create two directories with three files each in lexicographical order, + * then call FTS with a sort block that sorts in reverse lexicographical + * order. This has the least chance of getting a false positive due to + * differing file system semantics. UFS will return the files in the + * order they were created while ZFS will sort them lexicographically; in + * both cases, the order we expect is the reverse. + */ +ATF_TC(fts_blocks_test); +ATF_TC_HEAD(fts_blocks_test, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test FTS with a block in lieu of a comparison function"); +} +ATF_TC_BODY(fts_blocks_test, tc) +{ + char *args[] = { + "bar", "foo", NULL + }; + char *paths[] = { + "foo", "z", "y", "x", "foo", + "bar", "c", "b", "a", "bar", + NULL + }; + char **expect = paths; + FTS *fts; + FTSENT *ftse; + + ATF_REQUIRE_EQ(0, mkdir("bar", 0755)); + ATF_REQUIRE_EQ(0, close(creat("bar/a", 0644))); + ATF_REQUIRE_EQ(0, close(creat("bar/b", 0644))); + ATF_REQUIRE_EQ(0, close(creat("bar/c", 0644))); + ATF_REQUIRE_EQ(0, mkdir("foo", 0755)); + ATF_REQUIRE_EQ(0, close(creat("foo/x", 0644))); + ATF_REQUIRE_EQ(0, close(creat("foo/y", 0644))); + ATF_REQUIRE_EQ(0, close(creat("foo/z", 0644))); + fts = fts_open_b(args, 0, + ^(const FTSENT * const *a, const FTSENT * const *b) { + return (strcmp((*b)->fts_name, (*a)->fts_name)); + }); + ATF_REQUIRE_MSG(fts != NULL, "fts_open_b(): %m"); + while ((ftse = fts_read(fts)) != NULL && *expect != NULL) { + ATF_CHECK_STREQ(*expect, ftse->fts_name); + expect++; + } + ATF_CHECK_EQ(NULL, ftse); + ATF_CHECK_EQ(NULL, *expect); + ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, fts_blocks_test); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/fts_misc_test.c b/lib/libc/tests/gen/fts_misc_test.c new file mode 100644 index 000000000000..91640078f63c --- /dev/null +++ b/lib/libc/tests/gen/fts_misc_test.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <fcntl.h> +#include <fts.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "fts_test.h" + +ATF_TC(fts_unrdir); +ATF_TC_HEAD(fts_unrdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "unreadable directories"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(fts_unrdir, tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100)); + ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400)); + fts_test(tc, &(struct fts_testcase){ + (char *[]){ "dir", NULL }, + FTS_PHYSICAL, + (struct fts_expect[]){ + { FTS_D, "dir", "dir" }, + { FTS_D, "unr", "unr" }, + { FTS_DNR, "unr", "unr" }, + { FTS_D, "unx", "unx" }, + { FTS_DP, "unx", "unx" }, + { FTS_DP, "dir", "dir" }, + { 0 } + }, + }); +} + +ATF_TC(fts_unrdir_nochdir); +ATF_TC_HEAD(fts_unrdir_nochdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "unreadable directories (nochdir)"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(fts_unrdir_nochdir, tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100)); + ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400)); + fts_test(tc, &(struct fts_testcase){ + (char *[]){ "dir", NULL }, + FTS_PHYSICAL | FTS_NOCHDIR, + (struct fts_expect[]){ + { FTS_D, "dir", "dir" }, + { FTS_D, "unr", "dir/unr" }, + { FTS_DNR, "unr", "dir/unr" }, + { FTS_D, "unx", "dir/unx" }, + { FTS_DP, "unx", "dir/unx" }, + { FTS_DP, "dir", "dir" }, + { 0 } + }, + }); +} + +ATF_TP_ADD_TCS(tp) +{ + fts_check_debug(); + ATF_TP_ADD_TC(tp, fts_unrdir); + ATF_TP_ADD_TC(tp, fts_unrdir_nochdir); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/fts_options_test.c b/lib/libc/tests/gen/fts_options_test.c new file mode 100644 index 000000000000..fc3015138a49 --- /dev/null +++ b/lib/libc/tests/gen/fts_options_test.c @@ -0,0 +1,394 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <fcntl.h> +#include <fts.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "fts_test.h" + +static char *all_paths[] = { + "dir", + "dirl", + "file", + "filel", + "dead", + "noent", + NULL +}; + +/* + * Prepare the files and directories we will be inspecting. + */ +static void +fts_options_prepare(const struct atf_tc *tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, close(creat("file", 0644))); + ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644))); + ATF_REQUIRE_EQ(0, symlink("..", "dir/up")); + ATF_REQUIRE_EQ(0, symlink("dir", "dirl")); + ATF_REQUIRE_EQ(0, symlink("file", "filel")); + ATF_REQUIRE_EQ(0, symlink("noent", "dead")); +} + +ATF_TC(fts_options_logical); +ATF_TC_HEAD(fts_options_logical, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL"); +} +ATF_TC_BODY(fts_options_logical, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_LOGICAL, + (struct fts_expect[]){ + { FTS_DL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_F, "file", "dir/file" }, + { FTS_D, "up", "dir/up" }, + { FTS_DL, "dead", "dir/up/dead" }, + { FTS_DC, "dir", "dir/up/dir" }, + { FTS_DC, "dirl", "dir/up/dirl" }, + { FTS_F, "file", "dir/up/file" }, + { FTS_F, "filel", "dir/up/filel" }, + { FTS_DP, "up", "dir/up" }, + { FTS_DP, "dir", "dir" }, + { FTS_D, "dirl", "dirl" }, + { FTS_F, "file", "dirl/file" }, + { FTS_D, "up", "dirl/up" }, + { FTS_DL, "dead", "dirl/up/dead" }, + { FTS_DC, "dir", "dirl/up/dir" }, + { FTS_DC, "dirl", "dirl/up/dirl" }, + { FTS_F, "file", "dirl/up/file" }, + { FTS_F, "filel", "dirl/up/filel" }, + { FTS_DP, "up", "dirl/up" }, + { FTS_DP, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_F, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_logical_nostat); +ATF_TC_HEAD(fts_options_logical_nostat, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL | FTS_NOSTAT"); +} +ATF_TC_BODY(fts_options_logical_nostat, tc) +{ + /* + * While FTS_LOGICAL is not documented as being incompatible with + * FTS_NOSTAT, and FTS does not clear FTS_NOSTAT if FTS_LOGICAL is + * set, FTS_LOGICAL effectively nullifies FTS_NOSTAT by overriding + * the follow check in fts_stat(). In theory, FTS could easily be + * changed to only stat links (to check what they point to) in the + * FTS_LOGICAL | FTS_NOSTAT case, which would produce a different + * result here, so keep the test around in case that ever happens. + */ + atf_tc_expect_fail("FTS_LOGICAL nullifies FTS_NOSTAT"); + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_LOGICAL | FTS_NOSTAT, + (struct fts_expect[]){ + { FTS_DL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_NSOK, "file", "dir/file" }, + { FTS_D, "up", "dir/up" }, + { FTS_DL, "dead", "dir/up/dead" }, + { FTS_DC, "dir", "dir/up/dir" }, + { FTS_DC, "dirl", "dir/up/dirl" }, + { FTS_NSOK, "file", "dir/up/file" }, + { FTS_NSOK, "filel", "dir/up/filel" }, + { FTS_DP, "up", "dir/up" }, + { FTS_DP, "dir", "dir" }, + { FTS_D, "dirl", "dirl" }, + { FTS_NSOK, "file", "dirl/file" }, + { FTS_D, "up", "dirl/up" }, + { FTS_DL, "dead", "dirl/up/dead" }, + { FTS_DC, "dir", "dirl/up/dir" }, + { FTS_DC, "dirl", "dirl/up/dirl" }, + { FTS_NSOK, "file", "dirl/up/file" }, + { FTS_NSOK, "filel", "dirl/up/filel" }, + { FTS_DP, "up", "dirl/up" }, + { FTS_DP, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_F, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_logical_seedot); +ATF_TC_HEAD(fts_options_logical_seedot, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL | FTS_SEEDOT"); +} +ATF_TC_BODY(fts_options_logical_seedot, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_LOGICAL | FTS_SEEDOT, + (struct fts_expect[]){ + { FTS_DL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_DOT, ".", "dir/." }, + { FTS_DOT, "..", "dir/.." }, + { FTS_F, "file", "dir/file" }, + { FTS_D, "up", "dir/up" }, + { FTS_DOT, ".", "dir/up/." }, + { FTS_DOT, "..", "dir/up/.." }, + { FTS_DL, "dead", "dir/up/dead" }, + { FTS_DC, "dir", "dir/up/dir" }, + { FTS_DC, "dirl", "dir/up/dirl" }, + { FTS_F, "file", "dir/up/file" }, + { FTS_F, "filel", "dir/up/filel" }, + { FTS_DP, "up", "dir/up" }, + { FTS_DP, "dir", "dir" }, + { FTS_D, "dirl", "dirl" }, + { FTS_DOT, ".", "dirl/." }, + { FTS_DOT, "..", "dirl/.." }, + { FTS_F, "file", "dirl/file" }, + { FTS_D, "up", "dirl/up" }, + { FTS_DOT, ".", "dirl/up/." }, + { FTS_DOT, "..", "dirl/up/.." }, + { FTS_DL, "dead", "dirl/up/dead" }, + { FTS_DC, "dir", "dirl/up/dir" }, + { FTS_DC, "dirl", "dirl/up/dirl" }, + { FTS_F, "file", "dirl/up/file" }, + { FTS_F, "filel", "dirl/up/filel" }, + { FTS_DP, "up", "dirl/up" }, + { FTS_DP, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_F, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_physical); +ATF_TC_HEAD(fts_options_physical, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL"); +} +ATF_TC_BODY(fts_options_physical, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_PHYSICAL, + (struct fts_expect[]){ + { FTS_SL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_F, "file", "file" }, + { FTS_SL, "up", "up" }, + { FTS_DP, "dir", "dir" }, + { FTS_SL, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_physical_nochdir); +ATF_TC_HEAD(fts_options_physical_nochdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOCHDIR"); +} +ATF_TC_BODY(fts_options_physical_nochdir, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_PHYSICAL | FTS_NOCHDIR, + (struct fts_expect[]){ + { FTS_SL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_F, "file", "dir/file" }, + { FTS_SL, "up", "dir/up" }, + { FTS_DP, "dir", "dir" }, + { FTS_SL, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_physical_comfollow); +ATF_TC_HEAD(fts_options_physical_comfollow, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_COMFOLLOW"); +} +ATF_TC_BODY(fts_options_physical_comfollow, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_PHYSICAL | FTS_COMFOLLOW, + (struct fts_expect[]){ + { FTS_DL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_F, "file", "file" }, + { FTS_SL, "up", "up" }, + { FTS_DP, "dir", "dir" }, + { FTS_D, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "up", "up" }, + { FTS_DP, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_F, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_physical_comfollowdir); +ATF_TC_HEAD(fts_options_physical_comfollowdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_COMFOLLOWDIR"); +} +ATF_TC_BODY(fts_options_physical_comfollowdir, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_PHYSICAL | FTS_COMFOLLOWDIR, + (struct fts_expect[]){ + { FTS_DL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_F, "file", "file" }, + { FTS_SL, "up", "up" }, + { FTS_DP, "dir", "dir" }, + { FTS_D, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "up", "up" }, + { FTS_DP, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_physical_nostat); +ATF_TC_HEAD(fts_options_physical_nostat, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOSTAT"); +} +ATF_TC_BODY(fts_options_physical_nostat, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_PHYSICAL | FTS_NOSTAT, + (struct fts_expect[]){ + { FTS_SL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_NSOK, "file", "file" }, + { FTS_NSOK, "up", "up" }, + { FTS_DP, "dir", "dir" }, + { FTS_SL, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_physical_nostat_type); +ATF_TC_HEAD(fts_options_physical_nostat_type, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOSTAT_TYPE"); +} +ATF_TC_BODY(fts_options_physical_nostat_type, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_PHYSICAL | FTS_NOSTAT_TYPE, + (struct fts_expect[]){ + { FTS_SL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_F, "file", "file" }, + { FTS_SL, "up", "up" }, + { FTS_DP, "dir", "dir" }, + { FTS_SL, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +ATF_TC(fts_options_physical_seedot); +ATF_TC_HEAD(fts_options_physical_seedot, tc) +{ + atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_SEEDOT"); +} +ATF_TC_BODY(fts_options_physical_seedot, tc) +{ + fts_options_prepare(tc); + fts_test(tc, &(struct fts_testcase){ + all_paths, + FTS_PHYSICAL | FTS_SEEDOT, + (struct fts_expect[]){ + { FTS_SL, "dead", "dead" }, + { FTS_D, "dir", "dir" }, + { FTS_DOT, ".", "." }, + { FTS_DOT, "..", ".." }, + { FTS_F, "file", "file" }, + { FTS_SL, "up", "up" }, + { FTS_DP, "dir", "dir" }, + { FTS_SL, "dirl", "dirl" }, + { FTS_F, "file", "file" }, + { FTS_SL, "filel", "filel" }, + { FTS_NS, "noent", "noent" }, + { 0 } + }, + }); +} + +/* + * TODO: Add tests for FTS_XDEV and FTS_WHITEOUT + */ + +ATF_TP_ADD_TCS(tp) +{ + fts_check_debug(); + ATF_TP_ADD_TC(tp, fts_options_logical); + ATF_TP_ADD_TC(tp, fts_options_logical_nostat); + ATF_TP_ADD_TC(tp, fts_options_logical_seedot); + ATF_TP_ADD_TC(tp, fts_options_physical); + ATF_TP_ADD_TC(tp, fts_options_physical_nochdir); + ATF_TP_ADD_TC(tp, fts_options_physical_comfollow); + ATF_TP_ADD_TC(tp, fts_options_physical_comfollowdir); + ATF_TP_ADD_TC(tp, fts_options_physical_nostat); + ATF_TP_ADD_TC(tp, fts_options_physical_nostat_type); + ATF_TP_ADD_TC(tp, fts_options_physical_seedot); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/fts_test.h b/lib/libc/tests/gen/fts_test.h new file mode 100644 index 000000000000..b3f15050f265 --- /dev/null +++ b/lib/libc/tests/gen/fts_test.h @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef FTS_TEST_H_INCLUDED +#define FTS_TEST_H_INCLUDED + +struct fts_expect { + int fts_info; + const char *fts_name; + const char *fts_accpath; +}; + +struct fts_testcase { + char **paths; + int fts_options; + struct fts_expect *fts_expect; +}; + +/* shorter name for dead links */ +#define FTS_DL FTS_SLNONE + +/* are we being debugged? */ +static bool fts_test_debug; + +/* + * Set debug flag if appropriate. + */ +static void +fts_check_debug(void) +{ + fts_test_debug = !getenv("__RUNNING_INSIDE_ATF_RUN") && + isatty(STDERR_FILENO); +} + +/* + * Lexical order for reproducability. + */ +static int +fts_lexical_compar(const FTSENT * const *a, const FTSENT * const *b) +{ + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} + +/* + * Run FTS with the specified paths and options and verify that it + * produces the expected result in the correct order. + */ +static void +fts_test(const struct atf_tc *tc, const struct fts_testcase *fts_tc) +{ + FTS *fts; + FTSENT *ftse; + const struct fts_expect *expect = fts_tc->fts_expect; + long level = 0; + + fts = fts_open(fts_tc->paths, fts_tc->fts_options, fts_lexical_compar); + ATF_REQUIRE_MSG(fts != NULL, "fts_open(): %m"); + while ((ftse = fts_read(fts)) != NULL && expect->fts_name != NULL) { + if (expect->fts_info == FTS_DP || expect->fts_info == FTS_DNR) + level--; + if (fts_test_debug) { + fprintf(stderr, "%2ld %2d %s\n", level, + ftse->fts_info, ftse->fts_name); + } + ATF_CHECK_STREQ(expect->fts_name, ftse->fts_name); + ATF_CHECK_STREQ(expect->fts_accpath, ftse->fts_accpath); + ATF_CHECK_INTEQ(expect->fts_info, ftse->fts_info); + ATF_CHECK_INTEQ(level, ftse->fts_level); + if (expect->fts_info == FTS_D) + level++; + expect++; + } + ATF_CHECK_EQ(NULL, ftse); + ATF_CHECK_EQ(NULL, expect->fts_name); + ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m"); +} + +#endif /* FTS_TEST_H_INCLUDED */ diff --git a/lib/libc/tests/gen/glob2_test.c b/lib/libc/tests/gen/glob2_test.c index 8f69c36fece7..ff1b36b830b8 100644 --- a/lib/libc/tests/gen/glob2_test.c +++ b/lib/libc/tests/gen/glob2_test.c @@ -25,9 +25,12 @@ */ #include <sys/param.h> +#include <sys/stat.h> + #include <errno.h> #include <fcntl.h> #include <glob.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -102,10 +105,80 @@ ATF_TC_BODY(glob_pathological_test, tc) } } +ATF_TC(glob_period); +ATF_TC_HEAD(glob_period, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test behaviour when matching files that start with a period" + "(documented in the glob(3) CAVEATS section)."); +} +ATF_TC_BODY(glob_period, tc) +{ + int i; + glob_t g; + + atf_utils_create_file(".test", ""); + glob(".", 0, NULL, &g); + ATF_REQUIRE_MSG(g.gl_matchc == 1, + "glob(3) shouldn't match files starting with a period when using '.'"); + for (i = 0; i < g.gl_matchc; i++) + printf("%s\n", g.gl_pathv[i]); + glob(".*", 0, NULL, &g); + ATF_REQUIRE_MSG(g.gl_matchc == 3 && strcmp(g.gl_pathv[2], ".test") == 0, + "glob(3) should match files starting with a period when using '.*'"); +} + +static bool glob_callback_invoked; + +static int +errfunc(const char *path, int err) +{ + ATF_CHECK_STREQ(path, "test/"); + ATF_CHECK(err == EACCES); + glob_callback_invoked = true; + /* Suppress EACCES errors. */ + return (0); +} + +ATF_TC(glob_callback); +ATF_TC_HEAD(glob_callback, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test ability of callback function to suppress errors"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(glob_callback, tc) +{ + glob_t g; + int rv; + + ATF_REQUIRE_EQ(0, mkdir("test", 0755)); + ATF_REQUIRE_EQ(0, symlink("foo", "test/foo")); + ATF_REQUIRE_EQ(0, chmod("test", 0)); + + glob_callback_invoked = false; + rv = glob("test/*", 0, errfunc, &g); + ATF_CHECK_MSG(glob_callback_invoked, + "glob(3) failed to invoke callback function"); + ATF_CHECK_EQ_MSG(GLOB_NOMATCH, rv, + "callback function failed to suppress EACCES"); + globfree(&g); + + /* GLOB_ERR should ignore the suppressed error. */ + glob_callback_invoked = false; + rv = glob("test/*", GLOB_ERR, errfunc, &g); + ATF_CHECK_MSG(glob_callback_invoked, + "glob(3) failed to invoke callback function"); + ATF_CHECK_EQ_MSG(GLOB_ABORTED, rv, + "GLOB_ERR didn't override callback function"); + globfree(&g); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, glob_pathological_test); - + ATF_TP_ADD_TC(tp, glob_period); + ATF_TP_ADD_TC(tp, glob_callback); return (atf_no_error()); } diff --git a/lib/libc/tests/gen/glob_blocks_test.c b/lib/libc/tests/gen/glob_blocks_test.c new file mode 100644 index 000000000000..629b90bee762 --- /dev/null +++ b/lib/libc/tests/gen/glob_blocks_test.c @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <errno.h> +#include <glob.h> +#include <stdbool.h> + +#include <atf-c.h> + +ATF_TC(glob_b_callback); +ATF_TC_HEAD(glob_b_callback, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test ability of callback block to suppress errors"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(glob_b_callback, tc) +{ + static bool glob_callback_invoked; + static int (^errblk)(const char *, int) = + ^(const char *path, int err) { + ATF_CHECK_STREQ(path, "test/"); + ATF_CHECK(err == EACCES); + glob_callback_invoked = true; + /* Suppress EACCES errors. */ + return (0); + }; + glob_t g; + int rv; + + ATF_REQUIRE_EQ(0, mkdir("test", 0755)); + ATF_REQUIRE_EQ(0, symlink("foo", "test/foo")); + ATF_REQUIRE_EQ(0, chmod("test", 0)); + + glob_callback_invoked = false; + rv = glob_b("test/*", 0, errblk, &g); + ATF_CHECK_MSG(glob_callback_invoked, + "glob(3) failed to invoke callback block"); + ATF_CHECK_EQ_MSG(GLOB_NOMATCH, rv, + "callback function failed to suppress EACCES"); + globfree(&g); + + /* GLOB_ERR should ignore the suppressed error. */ + glob_callback_invoked = false; + rv = glob_b("test/*", GLOB_ERR, errblk, &g); + ATF_CHECK_MSG(glob_callback_invoked, + "glob(3) failed to invoke callback block"); + ATF_CHECK_EQ_MSG(GLOB_ABORTED, rv, + "GLOB_ERR didn't override callback block"); + globfree(&g); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, glob_b_callback); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/opendir_test.c b/lib/libc/tests/gen/opendir_test.c new file mode 100644 index 000000000000..b7481255654f --- /dev/null +++ b/lib/libc/tests/gen/opendir_test.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> + +#include <atf-c.h> + +/* + * Create a directory with a single subdirectory. + */ +static void +opendir_prepare(const struct atf_tc *tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, mkdir("dir/subdir", 0755)); +} + +/* + * Assuming dirp represents the directory created by opendir_prepare(), + * verify that readdir() returns what we expected to see there. + */ +static void +opendir_check(const struct atf_tc *tc, DIR *dirp) +{ + struct dirent *ent; + + ATF_REQUIRE((ent = readdir(dirp)) != NULL); + ATF_CHECK_EQ(1, ent->d_namlen); + ATF_CHECK_STREQ(".", ent->d_name); + ATF_CHECK_EQ(DT_DIR, ent->d_type); + ATF_REQUIRE((ent = readdir(dirp)) != NULL); + ATF_CHECK_EQ(2, ent->d_namlen); + ATF_CHECK_STREQ("..", ent->d_name); + ATF_CHECK_EQ(DT_DIR, ent->d_type); + ATF_REQUIRE((ent = readdir(dirp)) != NULL); + ATF_CHECK_EQ(sizeof("subdir") - 1, ent->d_namlen); + ATF_CHECK_STREQ("subdir", ent->d_name); + ATF_CHECK_EQ(DT_DIR, ent->d_type); + ATF_CHECK(readdir(dirp) == NULL); + ATF_CHECK(readdir(dirp) == NULL); +} + +ATF_TC(opendir_ok); +ATF_TC_HEAD(opendir_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Open a directory."); +} +ATF_TC_BODY(opendir_ok, tc) +{ + DIR *dirp; + + opendir_prepare(tc); + ATF_REQUIRE((dirp = opendir("dir")) != NULL); + opendir_check(tc, dirp); + ATF_CHECK_EQ(0, closedir(dirp)); +} + +ATF_TC(opendir_fifo); +ATF_TC_HEAD(opendir_fifo, tc) +{ + atf_tc_set_md_var(tc, "descr", "Do not hang if given a named pipe."); +} +ATF_TC_BODY(opendir_fifo, tc) +{ + DIR *dirp; + int fd; + + ATF_REQUIRE((fd = mkfifo("fifo", 0644)) >= 0); + ATF_REQUIRE_EQ(0, close(fd)); + ATF_REQUIRE((dirp = opendir("fifo")) == NULL); + ATF_CHECK_EQ(ENOTDIR, errno); +} + +ATF_TC(fdopendir_ok); +ATF_TC_HEAD(fdopendir_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Open a directory from a directory descriptor."); +} +ATF_TC_BODY(fdopendir_ok, tc) +{ + DIR *dirp; + int dd; + + opendir_prepare(tc); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE((dirp = fdopendir(dd)) != NULL); + opendir_check(tc, dirp); + ATF_CHECK_EQ(dd, fdclosedir(dirp)); + ATF_CHECK_EQ(0, close(dd)); +} + +ATF_TC(fdopendir_ebadf); +ATF_TC_HEAD(fdopendir_ebadf, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Open a directory from an invalid descriptor."); +} +ATF_TC_BODY(fdopendir_ebadf, tc) +{ + DIR *dirp; + int dd; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_CHECK_EQ(0, close(dd)); + ATF_REQUIRE((dirp = fdopendir(dd)) == NULL); + ATF_CHECK_EQ(EBADF, errno); +} + +ATF_TC(fdopendir_enotdir); +ATF_TC_HEAD(fdopendir_enotdir, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Open a directory from a non-directory descriptor."); +} +ATF_TC_BODY(fdopendir_enotdir, tc) +{ + DIR *dirp; + int fd; + + ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0); + ATF_REQUIRE((dirp = fdopendir(fd)) == NULL); + ATF_CHECK_EQ(ENOTDIR, errno); + ATF_CHECK_EQ(0, close(fd)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, opendir_ok); + ATF_TP_ADD_TC(tp, fdopendir_ok); + ATF_TP_ADD_TC(tp, fdopendir_ebadf); + ATF_TP_ADD_TC(tp, fdopendir_enotdir); + ATF_TP_ADD_TC(tp, opendir_fifo); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/posix_spawn_test.c b/lib/libc/tests/gen/posix_spawn_test.c index 77c3b5a569d9..22133cf1d59a 100644 --- a/lib/libc/tests/gen/posix_spawn_test.c +++ b/lib/libc/tests/gen/posix_spawn_test.c @@ -29,8 +29,11 @@ * IEEE Std. 1003.1-2008. */ +#include <sys/param.h> +#include <sys/stat.h> #include <sys/wait.h> #include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -38,6 +41,10 @@ #include <atf-c.h> +static const char true_script[] = + "#!/usr/bin/env\n" + "/usr/bin/true\n"; + char *myenv[2] = { "answer=42", NULL }; ATF_TC_WITHOUT_HEAD(posix_spawn_simple_test); @@ -124,6 +131,48 @@ ATF_TC_BODY(posix_spawnp_enoexec_fallback_null_argv0, tc) ATF_REQUIRE(error == EINVAL); } +ATF_TC(posix_spawnp_eacces); +ATF_TC_HEAD(posix_spawnp_eacces, tc) +{ + atf_tc_set_md_var(tc, "descr", "Verify EACCES behavior in posix_spawnp"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(posix_spawnp_eacces, tc) +{ + const struct spawnp_eacces_tc { + const char *pathvar; + int error_expected; + } spawnp_eacces_tests[] = { + { ".", EACCES }, /* File exists, but not +x */ + { "unsearchable", ENOENT }, /* File exists, dir not +x */ + }; + char *myargs[2] = { "eacces", NULL }; + int error; + + error = mkdir("unsearchable", 0755); + ATF_REQUIRE(error == 0); + error = symlink("/usr/bin/true", "unsearchable/eacces"); + ATF_REQUIRE(error == 0); + + (void)chmod("unsearchable", 0444); + + /* this will create a non-executable file */ + atf_utils_create_file("eacces", true_script); + + for (size_t i = 0; i < nitems(spawnp_eacces_tests); i++) { + const struct spawnp_eacces_tc *tc = &spawnp_eacces_tests[i]; + pid_t pid; + + error = setenv("PATH", tc->pathvar, 1); + ATF_REQUIRE_EQ(0, error); + + error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs, + myenv); + ATF_CHECK_INTEQ_MSG(tc->error_expected, error, + "path '%s'", tc->pathvar); + } +} + ATF_TP_ADD_TCS(tp) { @@ -131,6 +180,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, posix_spawn_no_such_command_negative_test); ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback); ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback_null_argv0); + ATF_TP_ADD_TC(tp, posix_spawnp_eacces); return (atf_no_error()); } diff --git a/lib/libc/tests/gen/realpath2_test.c b/lib/libc/tests/gen/realpath2_test.c index f89dd99cbb72..431df8721ae0 100644 --- a/lib/libc/tests/gen/realpath2_test.c +++ b/lib/libc/tests/gen/realpath2_test.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2017 Jan Kokemüller * All rights reserved. + * Copyright (c) 2025 Klara, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,6 +26,8 @@ */ #include <sys/param.h> +#include <sys/stat.h> + #include <errno.h> #include <fcntl.h> #include <stdio.h> @@ -34,6 +37,31 @@ #include <atf-c.h> +ATF_TC(realpath_null); +ATF_TC_HEAD(realpath_null, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test null input"); +} +ATF_TC_BODY(realpath_null, tc) +{ + ATF_REQUIRE_ERRNO(EINVAL, realpath(NULL, NULL) == NULL); +} + +ATF_TC(realpath_empty); +ATF_TC_HEAD(realpath_empty, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test empty input"); +} +ATF_TC_BODY(realpath_empty, tc) +{ + char resb[PATH_MAX] = ""; + + ATF_REQUIRE_EQ(0, mkdir("foo", 0755)); + ATF_REQUIRE_EQ(0, chdir("foo")); + ATF_REQUIRE_ERRNO(ENOENT, realpath("", resb) == NULL); + ATF_REQUIRE_STREQ("", resb); +} + ATF_TC(realpath_buffer_overflow); ATF_TC_HEAD(realpath_buffer_overflow, tc) { @@ -44,16 +72,11 @@ ATF_TC_HEAD(realpath_buffer_overflow, tc) ATF_TC_BODY(realpath_buffer_overflow, tc) { - char path[MAXPATHLEN] = { 0 }; - char resb[MAXPATHLEN] = { 0 }; - size_t i; + char path[PATH_MAX] = ""; + char resb[PATH_MAX] = ""; - path[0] = 'a'; + memset(path, 'a', sizeof(path) - 1); path[1] = '/'; - for (i = 2; i < sizeof(path) - 1; ++i) { - path[i] = 'a'; - } - ATF_REQUIRE(realpath(path, resb) == NULL); } @@ -66,9 +89,9 @@ ATF_TC_HEAD(realpath_empty_symlink, tc) ATF_TC_BODY(realpath_empty_symlink, tc) { - char path[MAXPATHLEN] = { 0 }; - char slnk[MAXPATHLEN] = { 0 }; - char resb[MAXPATHLEN] = { 0 }; + char path[PATH_MAX] = ""; + char slnk[PATH_MAX] = ""; + char resb[PATH_MAX] = ""; int fd; (void)strlcat(slnk, "empty_symlink", sizeof(slnk)); @@ -89,11 +112,70 @@ ATF_TC_BODY(realpath_empty_symlink, tc) ATF_REQUIRE(unlink(slnk) == 0); } -ATF_TP_ADD_TCS(tp) +ATF_TC(realpath_partial); +ATF_TC_HEAD(realpath_partial, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test that failure leaves a partial result"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} + +ATF_TC_BODY(realpath_partial, tc) { + char resb[PATH_MAX] = ""; + size_t len; + + /* scenario 1: missing directory */ + ATF_REQUIRE_EQ(0, mkdir("foo", 0755)); + ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL); + len = strnlen(resb, sizeof(resb)); + ATF_REQUIRE(len > 8 && len < sizeof(resb)); + ATF_REQUIRE_STREQ("/foo/bar", resb + len - 8); + + /* scenario 2: dead link 1 */ + ATF_REQUIRE_EQ(0, symlink("nix", "foo/bar")); + ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL); + len = strnlen(resb, sizeof(resb)); + ATF_REQUIRE(len > 8 && len < sizeof(resb)); + ATF_REQUIRE_STREQ("/foo/nix", resb + len - 8); + + /* scenario 3: missing file */ + ATF_REQUIRE_EQ(0, unlink("foo/bar")); + ATF_REQUIRE_EQ(0, mkdir("foo/bar", 0755)); + ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL); + len = strnlen(resb, sizeof(resb)); + ATF_REQUIRE(len > 12 && len < sizeof(resb)); + ATF_REQUIRE_STREQ("/foo/bar/baz", resb + len - 12); + + /* scenario 4: dead link 2 */ + ATF_REQUIRE_EQ(0, symlink("nix", "foo/bar/baz")); + ATF_REQUIRE_ERRNO(ENOENT, realpath("foo/bar/baz", resb) == NULL); + len = strnlen(resb, sizeof(resb)); + ATF_REQUIRE(len > 12 && len < sizeof(resb)); + ATF_REQUIRE_STREQ("/foo/bar/nix", resb + len - 12); + + /* scenario 5: unreadable directory */ + ATF_REQUIRE_EQ(0, chmod("foo", 000)); + ATF_REQUIRE_ERRNO(EACCES, realpath("foo/bar/baz", resb) == NULL); + len = strnlen(resb, sizeof(resb)); + ATF_REQUIRE(len > 4 && len < sizeof(resb)); + ATF_REQUIRE_STREQ("/foo", resb + len - 4); + + /* scenario 6: not a directory */ + ATF_REQUIRE_EQ(0, close(creat("bar", 0644))); + ATF_REQUIRE_ERRNO(ENOTDIR, realpath("bar/baz", resb) == NULL); + len = strnlen(resb, sizeof(resb)); + ATF_REQUIRE(len > 4 && len < sizeof(resb)); + ATF_REQUIRE_STREQ("/bar", resb + len - 4); +} +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, realpath_null); + ATF_TP_ADD_TC(tp, realpath_empty); ATF_TP_ADD_TC(tp, realpath_buffer_overflow); ATF_TP_ADD_TC(tp, realpath_empty_symlink); + ATF_TP_ADD_TC(tp, realpath_partial); return atf_no_error(); } diff --git a/lib/libc/tests/gen/scandir_blocks_test.c b/lib/libc/tests/gen/scandir_blocks_test.c new file mode 100644 index 000000000000..b94270bc410e --- /dev/null +++ b/lib/libc/tests/gen/scandir_blocks_test.c @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <dirent.h> +#include <fcntl.h> +#include <stdlib.h> + +#include <atf-c.h> + +static void +scandir_blocks_prepare(const struct atf_tc *tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755)); + ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644))); + ATF_REQUIRE_EQ(0, symlink("file", "dir/link")); + ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755)); +} + +static void +scandir_blocks_verify(const struct atf_tc *tc, int n, struct dirent **namelist) +{ + ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n); + ATF_CHECK_STREQ("link", namelist[0]->d_name); + ATF_CHECK_STREQ("file", namelist[1]->d_name); + ATF_CHECK_STREQ("dir", namelist[2]->d_name); + ATF_CHECK_STREQ("..", namelist[3]->d_name); + ATF_CHECK_STREQ(".", namelist[4]->d_name); +} + +ATF_TC(scandir_b_test); +ATF_TC_HEAD(scandir_b_test, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test scandir_b()"); +} +ATF_TC_BODY(scandir_b_test, tc) +{ + struct dirent **namelist = NULL; + int i, ret; + + scandir_blocks_prepare(tc); + ret = scandir_b("dir", &namelist, + ^(const struct dirent *ent) { + return (strcmp(ent->d_name, "skip") != 0); + }, + ^(const struct dirent **a, const struct dirent **b) { + return (strcmp((*b)->d_name, (*a)->d_name)); + }); + scandir_blocks_verify(tc, ret, namelist); + for (i = 0; i < ret; i++) + free(namelist[i]); + free(namelist); +} + +ATF_TC(fdscandir_b_test); +ATF_TC_HEAD(fdscandir_b_test, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fdscandir_b()"); +} +ATF_TC_BODY(fdscandir_b_test, tc) +{ + struct dirent **namelist = NULL; + int fd, i, ret; + + scandir_blocks_prepare(tc); + ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ret = fdscandir_b(fd, &namelist, + ^(const struct dirent *ent) { + return (strcmp(ent->d_name, "skip") != 0); + }, + ^(const struct dirent **a, const struct dirent **b) { + return (strcmp((*b)->d_name, (*a)->d_name)); + }); + scandir_blocks_verify(tc, ret, namelist); + for (i = 0; i < ret; i++) + free(namelist[i]); + free(namelist); + ATF_REQUIRE_EQ(0, close(fd)); +} + +ATF_TC(scandirat_b_test); +ATF_TC_HEAD(scandirat_b_test, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test scandirat_b()"); +} +ATF_TC_BODY(scandirat_b_test, tc) +{ + struct dirent **namelist = NULL; + int fd, i, ret; + + scandir_blocks_prepare(tc); + ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0); + ret = scandirat_b(fd, ".", &namelist, + ^(const struct dirent *ent) { + return (strcmp(ent->d_name, "skip") != 0); + }, + ^(const struct dirent **a, const struct dirent **b) { + return (strcmp((*b)->d_name, (*a)->d_name)); + }); + scandir_blocks_verify(tc, ret, namelist); + for (i = 0; i < ret; i++) + free(namelist[i]); + free(namelist); + ATF_REQUIRE_EQ(0, close(fd)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, scandir_b_test); + ATF_TP_ADD_TC(tp, fdscandir_b_test); + ATF_TP_ADD_TC(tp, scandirat_b_test); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/scandir_test.c b/lib/libc/tests/gen/scandir_test.c new file mode 100644 index 000000000000..afd25bf7c0b2 --- /dev/null +++ b/lib/libc/tests/gen/scandir_test.c @@ -0,0 +1,195 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> + +#include <atf-c.h> + +static void +scandir_prepare(const struct atf_tc *tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755)); + ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644))); + ATF_REQUIRE_EQ(0, symlink("file", "dir/link")); + ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755)); +} + +static void +scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist) +{ + ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n); + ATF_CHECK_STREQ("link", namelist[0]->d_name); + ATF_CHECK_STREQ("file", namelist[1]->d_name); + ATF_CHECK_STREQ("dir", namelist[2]->d_name); + ATF_CHECK_STREQ("..", namelist[3]->d_name); + ATF_CHECK_STREQ(".", namelist[4]->d_name); +} + +static int +scandir_select(const struct dirent *ent) +{ + return (strcmp(ent->d_name, "skip") != 0); +} + +static int +scandir_compare(const struct dirent **a, const struct dirent **b) +{ + return (strcmp((*b)->d_name, (*a)->d_name)); +} + +ATF_TC(scandir_test); +ATF_TC_HEAD(scandir_test, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test scandir()"); +} +ATF_TC_BODY(scandir_test, tc) +{ + struct dirent **namelist = NULL; + int i, ret; + + scandir_prepare(tc); + ret = scandir("dir", &namelist, scandir_select, scandir_compare); + scandir_verify(tc, ret, namelist); + for (i = 0; i < ret; i++) + free(namelist[i]); + free(namelist); +} + +ATF_TC(fdscandir_test); +ATF_TC_HEAD(fdscandir_test, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test fdscandir()"); +} +ATF_TC_BODY(fdscandir_test, tc) +{ + struct dirent **namelist = NULL; + int fd, i, ret; + + scandir_prepare(tc); + ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ret = fdscandir(fd, &namelist, scandir_select, scandir_compare); + scandir_verify(tc, ret, namelist); + for (i = 0; i < ret; i++) + free(namelist[i]); + free(namelist); + ATF_REQUIRE_EQ(0, close(fd)); +} + +ATF_TC(scandirat_test); +ATF_TC_HEAD(scandirat_test, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test scandirat()"); +} +ATF_TC_BODY(scandirat_test, tc) +{ + struct dirent **namelist = NULL; + int fd, i, ret; + + scandir_prepare(tc); + ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0); + ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare); + scandir_verify(tc, ret, namelist); + for (i = 0; i < ret; i++) + free(namelist[i]); + free(namelist); + ATF_REQUIRE_EQ(0, close(fd)); +} + +static int +scandir_none(const struct dirent *ent __unused) +{ + return (0); +} + +ATF_TC(scandir_none); +ATF_TC_HEAD(scandir_none, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test scandir() when no entries are selected"); +} +ATF_TC_BODY(scandir_none, tc) +{ + struct dirent **namelist = NULL; + + ATF_REQUIRE_EQ(0, scandir(".", &namelist, scandir_none, alphasort)); + ATF_REQUIRE(namelist); + free(namelist); +} + +/* + * Test that scandir() propagates errors from readdir(): we create a + * directory with enough entries that it can't be read in a single + * getdirentries() call, then abuse the selection callback to close the + * file descriptor scandir() is using after the first call, causing the + * next one to fail, and verify that readdir() returns an error instead of + * a partial result. We make two passes, one in which nothing was + * selected before the error occurred, and one in which everything was. + */ +static int scandir_error_count; +static int scandir_error_fd; +static int scandir_error_select_return; + +static int +scandir_error_select(const struct dirent *ent __unused) +{ + if (scandir_error_count++ == 0) + close(scandir_error_fd); + return (scandir_error_select_return); +} + +ATF_TC(scandir_error); +ATF_TC_HEAD(scandir_error, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test that scandir() propagates errors from readdir()"); +} +ATF_TC_BODY(scandir_error, tc) +{ + char path[16]; + struct dirent **namelist = NULL; + int fd, i; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + for (i = 0; i < 1024; i++) { + snprintf(path, sizeof(path), "dir/%04x", i); + ATF_REQUIRE_EQ(0, symlink(path + 4, path)); + } + + /* first pass, select nothing */ + ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + scandir_error_count = 0; + scandir_error_fd = fd; + scandir_error_select_return = 0; + ATF_CHECK_ERRNO(EBADF, + fdscandir(fd, &namelist, scandir_error_select, NULL) < 0); + ATF_CHECK_EQ(NULL, namelist); + + /* second pass, select everything */ + ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + scandir_error_count = 0; + scandir_error_fd = fd; + scandir_error_select_return = 1; + ATF_CHECK_ERRNO(EBADF, + fdscandir(fd, &namelist, scandir_error_select, NULL) < 0); + ATF_CHECK_EQ(NULL, namelist); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, scandir_test); + ATF_TP_ADD_TC(tp, fdscandir_test); + ATF_TP_ADD_TC(tp, scandirat_test); + ATF_TP_ADD_TC(tp, scandir_none); + ATF_TP_ADD_TC(tp, scandir_error); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/sig2str_test.c b/lib/libc/tests/gen/sig2str_test.c new file mode 100644 index 000000000000..00b6ebb2349a --- /dev/null +++ b/lib/libc/tests/gen/sig2str_test.c @@ -0,0 +1,213 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Ricardo Branco <rbranco@suse.de> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +static void +test_roundtrip(int signum) +{ + char str[SIG2STR_MAX]; + int sig; + + ATF_REQUIRE_EQ_MSG(sig2str(signum, str), 0, + "sig2str(%d) failed", signum); + ATF_REQUIRE_EQ_MSG(str2sig(str, &sig), 0, + "str2sig(\"%s\") failed", str); + ATF_REQUIRE_INTEQ_MSG(sig, signum, + "Mismatch: roundtrip conversion gave %d instead of %d", + sig, signum); +} + +ATF_TC_WITHOUT_HEAD(sig2str_valid); +ATF_TC_BODY(sig2str_valid, tc) +{ + int sig; + + for (sig = 1; sig < sys_nsig; sig++) { + test_roundtrip(sig); + } +} + +ATF_TC_WITHOUT_HEAD(sig2str_invalid); +ATF_TC_BODY(sig2str_invalid, tc) +{ + char buf[SIG2STR_MAX]; + + ATF_CHECK(sig2str(0, buf) != 0); + ATF_CHECK(sig2str(-1, buf) != 0); + ATF_CHECK(sig2str(SIGRTMAX + 1, buf) != 0); +} + +ATF_TC_WITHOUT_HEAD(str2sig_rtmin_rtmax); +ATF_TC_BODY(str2sig_rtmin_rtmax, tc) +{ + int sig; + + ATF_CHECK_MSG(str2sig("RTMIN", &sig) == 0, + "str2sig(\"RTMIN\") failed"); + ATF_CHECK_INTEQ_MSG(sig, SIGRTMIN, + "RTMIN mapped to %d, expected %d", sig, SIGRTMIN); + + ATF_CHECK_MSG(str2sig("RTMAX", &sig) == 0, + "str2sig(\"RTMAX\") failed"); + ATF_CHECK_INTEQ_MSG(sig, SIGRTMAX, + "RTMAX mapped to %d, expected %d", sig, SIGRTMAX); + + ATF_CHECK_MSG(str2sig("RTMIN+1", &sig) == 0, + "str2sig(\"RTMIN+1\") failed"); + ATF_CHECK_INTEQ_MSG(sig, SIGRTMIN + 1, + "RTMIN+1 mapped to %d, expected %d", sig, SIGRTMIN + 1); + + ATF_CHECK_MSG(str2sig("RTMAX-1", &sig) == 0, + "str2sig(\"RTMAX-1\") failed"); + ATF_CHECK_INTEQ_MSG(sig, SIGRTMAX - 1, + "RTMAX-1 mapped to %d, expected %d", sig, SIGRTMAX - 1); +} + +ATF_TC_WITHOUT_HEAD(str2sig_invalid_rt); +ATF_TC_BODY(str2sig_invalid_rt, tc) +{ + int i, sig; + + const char *invalid[] = { + "RTMIN+0", + "RTMAX-0", + "RTMIN-777", + "RTMIN+777", + "RTMAX-777", + "RTMAX+777", + "RTMIN-", + "RTMAX-", + "RTMIN0", + "RTMAX1", + "RTMIN+abc", + "RTMIN-abc", + NULL + }; + + for (i = 0; invalid[i] != NULL; i++) { + ATF_CHECK_MSG(str2sig(invalid[i], &sig) != 0, + "str2sig(\"%s\") unexpectedly succeeded", invalid[i]); + } +} + +ATF_TC_WITHOUT_HEAD(str2sig_fullname); +ATF_TC_BODY(str2sig_fullname, tc) +{ + char fullname[SIG2STR_MAX + 3]; + int n, sig; + + for (sig = 1; sig < sys_nsig; sig++) { + snprintf(fullname, sizeof(fullname), "SIG%s", sys_signame[sig]); + + ATF_CHECK_MSG(str2sig(fullname, &n) == 0, + "str2sig(\"%s\") failed with errno %d (%s)", + fullname, errno, strerror(errno)); + + ATF_CHECK_INTEQ_MSG(n, sig, + "Mismatch: %s = %d, str2sig(\"%s\") = %d", + sys_signame[sig], sig, fullname, n); + } +} + +ATF_TC_WITHOUT_HEAD(str2sig_lowercase); +ATF_TC_BODY(str2sig_lowercase, tc) +{ + char fullname[SIG2STR_MAX + 3]; + int n, sig; + + for (sig = 1; sig < sys_nsig; sig++) { + snprintf(fullname, sizeof(fullname), "sig%s", sys_signame[sig]); + for (size_t i = 3; i < strlen(fullname); i++) + fullname[i] = toupper((unsigned char)fullname[i]); + + ATF_CHECK_MSG(str2sig(fullname, &n) == 0, + "str2sig(\"%s\") failed with errno %d (%s)", + fullname, errno, strerror(errno)); + + ATF_CHECK_INTEQ_MSG(n, sig, + "Mismatch: %s = %d, str2sig(\"%s\") = %d", + sys_signame[sig], sig, fullname, n); + } +} + +ATF_TC_WITHOUT_HEAD(str2sig_numeric); +ATF_TC_BODY(str2sig_numeric, tc) +{ + char buf[16]; + int n, sig; + + for (sig = NSIG; sig < SIGRTMIN; sig++) { + snprintf(buf, sizeof(buf), "%d", sig); + ATF_CHECK_MSG(str2sig(buf, &n) == 0, + "str2sig(\"%s\") failed", buf); + ATF_CHECK_INTEQ_MSG(n, sig, + "Mismatch: str2sig(\"%s\") = %d, expected %d", + buf, n, sig); + } +} + +ATF_TC_WITHOUT_HEAD(str2sig_invalid); +ATF_TC_BODY(str2sig_invalid, tc) +{ + const char *invalid[] = { + "SIGDOESNOTEXIST", + "DOESNOTEXIST", + "INTERRUPT", + "", + "SIG", + "123abc", + "sig1extra", + NULL + }; + int i, sig; + + for (i = 0; invalid[i] != NULL; i++) { + ATF_CHECK_MSG(str2sig(invalid[i], &sig) != 0, + "str2sig(\"%s\") unexpectedly succeeded", invalid[i]); + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, sig2str_valid); + ATF_TP_ADD_TC(tp, sig2str_invalid); + ATF_TP_ADD_TC(tp, str2sig_rtmin_rtmax); + ATF_TP_ADD_TC(tp, str2sig_invalid_rt); + ATF_TP_ADD_TC(tp, str2sig_fullname); + ATF_TP_ADD_TC(tp, str2sig_lowercase); + ATF_TP_ADD_TC(tp, str2sig_numeric); + ATF_TP_ADD_TC(tp, str2sig_invalid); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/wordexp_test.c b/lib/libc/tests/gen/wordexp_test.c index 909097fbf51e..a8b9d5509633 100644 --- a/lib/libc/tests/gen/wordexp_test.c +++ b/lib/libc/tests/gen/wordexp_test.c @@ -292,6 +292,31 @@ ATF_TC_BODY(with_SIGCHILD_handler_test, tc) ATF_REQUIRE(r == 0); } +ATF_TC_WITHOUT_HEAD(with_SIGCHILD_ignore_test); +ATF_TC_BODY(with_SIGCHILD_ignore_test, tc) +{ + struct sigaction sa; + wordexp_t we; + int r; + + /* With SIGCHLD set to ignore so that the kernel auto-reaps zombies. */ + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + r = sigaction(SIGCHLD, &sa, NULL); + ATF_REQUIRE(r == 0); + r = wordexp("hello world", &we, 0); + ATF_REQUIRE(r == 0); + ATF_REQUIRE(we.we_wordc == 2); + ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0); + ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0); + ATF_REQUIRE(we.we_wordv[2] == NULL); + wordfree(&we); + sa.sa_handler = SIG_DFL; + r = sigaction(SIGCHLD, &sa, NULL); + ATF_REQUIRE(r == 0); +} + ATF_TC_WITHOUT_HEAD(with_unused_non_default_IFS_test); ATF_TC_BODY(with_unused_non_default_IFS_test, tc) { @@ -350,6 +375,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, WRDE_BADCHAR_test); ATF_TP_ADD_TC(tp, WRDE_SYNTAX_test); ATF_TP_ADD_TC(tp, with_SIGCHILD_handler_test); + ATF_TP_ADD_TC(tp, with_SIGCHILD_ignore_test); ATF_TP_ADD_TC(tp, with_unused_non_default_IFS_test); ATF_TP_ADD_TC(tp, with_used_non_default_IFS_test); diff --git a/lib/libc/tests/locale/wctomb_test.c b/lib/libc/tests/locale/wctomb_test.c index 1e142ed74c48..ef2a6dcbe1e3 100644 --- a/lib/libc/tests/locale/wctomb_test.c +++ b/lib/libc/tests/locale/wctomb_test.c @@ -41,6 +41,18 @@ #include <atf-c.h> +ATF_TC_WITHOUT_HEAD(euccs1_test); +ATF_TC_BODY(euccs1_test, tc) +{ + wchar_t wc = 0x8e000000; + char buf[MB_LEN_MAX]; + + ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "zh_CN.eucCN"), + "zh_CN.eucCN") == 0); + + ATF_REQUIRE(wctomb(&buf[0], wc) == 4); +} + ATF_TC_WITHOUT_HEAD(wctomb_test); ATF_TC_BODY(wctomb_test, tc) { @@ -104,6 +116,7 @@ ATF_TC_BODY(wctomb_test, tc) ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, euccs1_test); ATF_TP_ADD_TC(tp, wctomb_test); return (atf_no_error()); diff --git a/lib/libc/tests/net/Makefile b/lib/libc/tests/net/Makefile index 016f0cd82ceb..ec0668633508 100644 --- a/lib/libc/tests/net/Makefile +++ b/lib/libc/tests/net/Makefile @@ -3,6 +3,11 @@ PACKAGE= tests ATF_TESTS_C+= ether_test ATF_TESTS_C+= eui64_aton_test ATF_TESTS_C+= eui64_ntoa_test +ATF_TESTS_CXX+= link_addr_test +ATF_TESTS_CXX+= inet_net_test + +CXXSTD.link_addr_test= c++20 +CXXSTD.inet_net_test= c++20 CFLAGS+= -I${.CURDIR} diff --git a/lib/libc/tests/net/getaddrinfo/Makefile b/lib/libc/tests/net/getaddrinfo/Makefile index 1c8d68d8cc25..1299e91615b7 100644 --- a/lib/libc/tests/net/getaddrinfo/Makefile +++ b/lib/libc/tests/net/getaddrinfo/Makefile @@ -1,12 +1,10 @@ PACKAGE= tests -TESTSRC= ${SRCTOP}/contrib/netbsd-tests/lib/libc/net/${.CURDIR:T} - .include <bsd.own.mk> BINDIR= ${TESTSDIR} -NETBSD_ATF_TESTS_SH= getaddrinfo_test +ATF_TESTS_SH= getaddrinfo_test ATF_TESTS_C= getaddrinfo PROGS= h_gai @@ -16,13 +14,33 @@ ${PACKAGE}DATA_FILESPACKAGE= tests ${PACKAGE}DATA_FILESDIR= ${TESTSDIR}/data -${PACKAGE}DATA_FILES+= basics_v4.exp basics_v4v6.exp -${PACKAGE}DATA_FILES+= no_host_v4.exp no_host_v4v6.exp -${PACKAGE}DATA_FILES+= no_serv_v4.exp no_serv_v4v6.exp -${PACKAGE}DATA_FILES+= sock_raw_v4.exp sock_raw_v4v6.exp -${PACKAGE}DATA_FILES+= spec_fam_v4.exp spec_fam_v4v6.exp -${PACKAGE}DATA_FILES+= scoped.exp -${PACKAGE}DATA_FILES+= unsup_fam.exp +${PACKAGE}DATA_FILES+= data/basics_v4.exp +${PACKAGE}DATA_FILES+= data/basics_v4_only.exp +${PACKAGE}DATA_FILES+= data/basics_v4v6.exp +${PACKAGE}DATA_FILES+= data/basics_v4v6_prefer_v4.exp +${PACKAGE}DATA_FILES+= data/no_host_v4.exp +${PACKAGE}DATA_FILES+= data/no_host_v4_only.exp +${PACKAGE}DATA_FILES+= data/no_host_v4v6.exp +${PACKAGE}DATA_FILES+= data/no_host_v4v6_prefer_v4.exp +${PACKAGE}DATA_FILES+= data/no_serv_v4.exp +${PACKAGE}DATA_FILES+= data/no_serv_v4_only.exp +${PACKAGE}DATA_FILES+= data/no_serv_v4v6.exp +${PACKAGE}DATA_FILES+= data/no_serv_v4v6_prefer_v4.exp +${PACKAGE}DATA_FILES+= data/scoped.exp +${PACKAGE}DATA_FILES+= data/scoped_v4_only.exp +${PACKAGE}DATA_FILES+= data/scoped_v4v6_prefer_v4.exp +${PACKAGE}DATA_FILES+= data/sock_raw_v4.exp +${PACKAGE}DATA_FILES+= data/sock_raw_v4_only.exp +${PACKAGE}DATA_FILES+= data/sock_raw_v4v6.exp +${PACKAGE}DATA_FILES+= data/sock_raw_v4v6_prefer_v4.exp +${PACKAGE}DATA_FILES+= data/spec_fam_v4.exp +${PACKAGE}DATA_FILES+= data/spec_fam_v4_only.exp +${PACKAGE}DATA_FILES+= data/spec_fam_v4v6.exp +${PACKAGE}DATA_FILES+= data/spec_fam_v4v6_prefer_v4.exp +${PACKAGE}DATA_FILES+= data/unsup_fam.exp +${PACKAGE}DATA_FILES+= data/unsup_fam_v4_only.exp +${PACKAGE}DATA_FILES+= data/unsup_fam_v4v6_prefer_v4.exp + .include "../../Makefile.netbsd-tests" diff --git a/lib/libc/tests/net/getaddrinfo/basics_v4.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4.exp index 4f1ee3517211..4f1ee3517211 100644 --- a/lib/libc/tests/net/getaddrinfo/basics_v4.exp +++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/basics_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4_only.exp new file mode 100644 index 000000000000..0a37d3212649 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4_only.exp @@ -0,0 +1,50 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv http +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv http +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http +ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv tftp +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv tftp +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv tftp +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp +ai3: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp +ai4: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv echo +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv echo +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv echo +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo +ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo +ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo +ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo + diff --git a/lib/libc/tests/net/getaddrinfo/basics_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6.exp index b74758e93e2e..b74758e93e2e 100644 --- a/lib/libc/tests/net/getaddrinfo/basics_v4v6.exp +++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/basics_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6_prefer_v4.exp new file mode 100644 index 000000000000..0a37d3212649 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6_prefer_v4.exp @@ -0,0 +1,50 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv http +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv http +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http +ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv tftp +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv tftp +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv tftp +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp +ai3: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp +ai4: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv echo +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv echo +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv echo +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo +ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo +ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo +ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo + diff --git a/lib/libc/tests/net/getaddrinfo/data/generate_testdata.sh b/lib/libc/tests/net/getaddrinfo/data/generate_testdata.sh new file mode 100755 index 000000000000..f0425a3b0283 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/generate_testdata.sh @@ -0,0 +1,45 @@ +#service ip6addrctl prefer_ipv4 +TEST=./h_gai +family=v4_only + +( $TEST ::1 http + $TEST 127.0.0.1 http + $TEST localhost http + $TEST ::1 tftp + $TEST 127.0.0.1 tftp + $TEST localhost tftp + $TEST ::1 echo + $TEST 127.0.0.1 echo + $TEST localhost echo ) > basics_${family}.exp + +( $TEST -4 localhost http + $TEST -6 localhost http ) > spec_fam_${family}.exp + +( $TEST '' http + $TEST '' echo + $TEST '' tftp + $TEST '' 80 + $TEST -P '' http + $TEST -P '' echo + $TEST -P '' tftp + $TEST -P '' 80 + $TEST -S '' 80 + $TEST -D '' 80 ) > no_host_${family}.exp + +( $TEST ::1 '' + $TEST 127.0.0.1 '' + $TEST localhost '' + $TEST '' '' ) > no_serv_${family}.exp + +( $TEST -R -p 0 localhost '' + $TEST -R -p 59 localhost '' + $TEST -R -p 59 localhost 80 + $TEST -R -p 59 localhost www + $TEST -R -p 59 ::1 '' ) > sock_raw_${family}.exp + +( $TEST -f 99 localhost '' ) > unsup_fam_${family}.exp + +( $TEST fe80::1%lo0 http +# IF=`ifconfig -a | grep -v '^ ' | sed -e 's/:.*//' | head -1 | awk '{print $1}'` +# $TEST fe80::1%$IF http +) > scoped_${family}.exp diff --git a/lib/libc/tests/net/getaddrinfo/no_host_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4.exp index 7ed41cd9d88a..7ed41cd9d88a 100644 --- a/lib/libc/tests/net/getaddrinfo/no_host_v4.exp +++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4.exp diff --git a/lib/libc/tests/net/getaddrinfo/no_host_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4_only.exp index 596799305117..596799305117 100644 --- a/lib/libc/tests/net/getaddrinfo/no_host_v4v6.exp +++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4_only.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6.exp new file mode 100644 index 000000000000..596799305117 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6.exp @@ -0,0 +1,68 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http +ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo +ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo +ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo +ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp +ai3: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp +ai4: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http +ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http +ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http +ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http +ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http +ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv echo +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv echo +ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv echo +ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv echo +ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv echo +ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv echo + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv tftp +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv tftp +ai3: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv tftp +ai4: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv tftp + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http +ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http +ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http +ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http +ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http + +arg: flags 0x2 family 0 socktype 1 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 0 socktype 2 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http + diff --git a/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6_prefer_v4.exp new file mode 100644 index 000000000000..596799305117 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6_prefer_v4.exp @@ -0,0 +1,68 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http +ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo +ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo +ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo +ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp +ai3: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp +ai4: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http +ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http +ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http +ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http +ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http +ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv echo +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv echo +ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv echo +ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv echo +ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv echo +ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv echo + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv tftp +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv tftp +ai3: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv tftp +ai4: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv tftp + +arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http +ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http +ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http +ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http +ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http +ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http + +arg: flags 0x2 family 0 socktype 1 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 0 socktype 2 protocol 0 addrlen 0 host (empty) serv 80 +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http + diff --git a/lib/libc/tests/net/getaddrinfo/no_serv_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4.exp index 667234d2161f..667234d2161f 100644 --- a/lib/libc/tests/net/getaddrinfo/no_serv_v4.exp +++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/no_serv_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4_only.exp new file mode 100644 index 000000000000..0d28490c8d81 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4_only.exp @@ -0,0 +1,20 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv (empty) +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0 +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0 +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv (empty) +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0 +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0 + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv (empty) +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0 +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0 +ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0 +ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0 +ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty) +Name does not resolve diff --git a/lib/libc/tests/net/getaddrinfo/no_serv_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6.exp index 53502547c40e..53502547c40e 100644 --- a/lib/libc/tests/net/getaddrinfo/no_serv_v4v6.exp +++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6_prefer_v4.exp new file mode 100644 index 000000000000..0d28490c8d81 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6_prefer_v4.exp @@ -0,0 +1,20 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv (empty) +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0 +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0 +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv (empty) +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0 +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0 + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv (empty) +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0 +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0 +ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0 +ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0 +ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty) +Name does not resolve diff --git a/lib/libc/tests/net/getaddrinfo/scoped.exp b/lib/libc/tests/net/getaddrinfo/data/scoped.exp index f5ddb4bf6feb..f5ddb4bf6feb 100644 --- a/lib/libc/tests/net/getaddrinfo/scoped.exp +++ b/lib/libc/tests/net/getaddrinfo/data/scoped.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/scoped_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/scoped_v4_only.exp new file mode 100644 index 000000000000..f5ddb4bf6feb --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/scoped_v4_only.exp @@ -0,0 +1,5 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host fe80::1%lo0 serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host fe80::1%lo0 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host fe80::1%lo0 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host fe80::1%lo0 serv http + diff --git a/lib/libc/tests/net/getaddrinfo/data/scoped_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/scoped_v4v6_prefer_v4.exp new file mode 100644 index 000000000000..f5ddb4bf6feb --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/scoped_v4v6_prefer_v4.exp @@ -0,0 +1,5 @@ +arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host fe80::1%lo0 serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host fe80::1%lo0 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host fe80::1%lo0 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host fe80::1%lo0 serv http + diff --git a/lib/libc/tests/net/getaddrinfo/sock_raw_v4.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4.exp index 021038b81dcc..021038b81dcc 100644 --- a/lib/libc/tests/net/getaddrinfo/sock_raw_v4.exp +++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4_only.exp new file mode 100644 index 000000000000..932c1faab0d3 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4_only.exp @@ -0,0 +1,15 @@ +arg: flags 0x2 family 0 socktype 3 protocol 0 addrlen 0 host localhost serv (empty) +ai1: flags 0x2 family 2 socktype 3 protocol 0 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 28 socktype 3 protocol 0 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv (empty) +ai1: flags 0x2 family 2 socktype 3 protocol 59 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv 80 +Service was not recognized for socket type +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv www +Service was not recognized for socket type +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host ::1 serv (empty) +ai1: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0 + diff --git a/lib/libc/tests/net/getaddrinfo/sock_raw_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6.exp index e494271a3d35..e494271a3d35 100644 --- a/lib/libc/tests/net/getaddrinfo/sock_raw_v4v6.exp +++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6_prefer_v4.exp new file mode 100644 index 000000000000..932c1faab0d3 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6_prefer_v4.exp @@ -0,0 +1,15 @@ +arg: flags 0x2 family 0 socktype 3 protocol 0 addrlen 0 host localhost serv (empty) +ai1: flags 0x2 family 2 socktype 3 protocol 0 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 28 socktype 3 protocol 0 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv (empty) +ai1: flags 0x2 family 2 socktype 3 protocol 59 addrlen 16 host 127.0.0.1 serv 0 +ai2: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0 + +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv 80 +Service was not recognized for socket type +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv www +Service was not recognized for socket type +arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host ::1 serv (empty) +ai1: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0 + diff --git a/lib/libc/tests/net/getaddrinfo/spec_fam_v4.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4.exp index 924d2a16f47c..924d2a16f47c 100644 --- a/lib/libc/tests/net/getaddrinfo/spec_fam_v4.exp +++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4.exp diff --git a/lib/libc/tests/net/getaddrinfo/spec_fam_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4_only.exp index af3506938503..af3506938503 100644 --- a/lib/libc/tests/net/getaddrinfo/spec_fam_v4v6.exp +++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4_only.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6.exp new file mode 100644 index 000000000000..af3506938503 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6.exp @@ -0,0 +1,10 @@ +arg: flags 0x2 family 2 socktype 0 protocol 0 addrlen 0 host localhost serv http +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 28 socktype 0 protocol 0 addrlen 0 host localhost serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http + diff --git a/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6_prefer_v4.exp new file mode 100644 index 000000000000..af3506938503 --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6_prefer_v4.exp @@ -0,0 +1,10 @@ +arg: flags 0x2 family 2 socktype 0 protocol 0 addrlen 0 host localhost serv http +ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http +ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http +ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http + +arg: flags 0x2 family 28 socktype 0 protocol 0 addrlen 0 host localhost serv http +ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http +ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http +ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http + diff --git a/lib/libc/tests/net/getaddrinfo/unsup_fam.exp b/lib/libc/tests/net/getaddrinfo/data/unsup_fam.exp index 69e6b48a854b..69e6b48a854b 100644 --- a/lib/libc/tests/net/getaddrinfo/unsup_fam.exp +++ b/lib/libc/tests/net/getaddrinfo/data/unsup_fam.exp diff --git a/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4_only.exp new file mode 100644 index 000000000000..69e6b48a854b --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4_only.exp @@ -0,0 +1,2 @@ +arg: flags 0x2 family 99 socktype 0 protocol 0 addrlen 0 host localhost serv (empty) +Address family not recognized diff --git a/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4v6_prefer_v4.exp new file mode 100644 index 000000000000..69e6b48a854b --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4v6_prefer_v4.exp @@ -0,0 +1,2 @@ +arg: flags 0x2 family 99 socktype 0 protocol 0 addrlen 0 host localhost serv (empty) +Address family not recognized diff --git a/lib/libc/tests/net/getaddrinfo/getaddrinfo_test.sh b/lib/libc/tests/net/getaddrinfo/getaddrinfo_test.sh new file mode 100755 index 000000000000..dd17ab2e3f4a --- /dev/null +++ b/lib/libc/tests/net/getaddrinfo/getaddrinfo_test.sh @@ -0,0 +1,443 @@ +# $NetBSD: t_getaddrinfo.sh,v 1.2 2011/06/15 07:54:32 jmmv Exp $ + +# +# Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, and 2002 WIDE Project. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the project nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +if [ "$(sysctl -i -n kern.features.vimage)" != 1 ]; then + atf_skip "This test requires VIMAGE" +fi + +vnet_mkjail() +{ + jailname=getaddrinfo_test_$1 + jail -c name=${jailname} persist vnet + ifconfig -j ${jailname} lo0 inet 127.0.0.1/8 + # For those machines not support IPv6 + ifconfig -j ${jailname} lo0 inet6 ::1/64 || true + service -j ${jailname} ip6addrctl $2 || true +} + +vnet_cleanup() +{ + jailname=getaddrinfo_test_$1 + jail -r ${jailname} +} + +check_output() +{ + if [ "$2" = "none" ]; then + if [ "$3" = "prefer_v6" ]; then + exp="${1}.exp" + else + exp="${1}_v4_only.exp" + fi + elif [ "$2" = "hosts" ]; then + lcl=$(cat /etc/hosts | sed -e 's/#.*$//' -e 's/[ ][ ]*/ /g' | awk '/ localhost($| )/ {printf "%s ", $1}') + if [ "${lcl%*::*}" = "${lcl}" ]; then + exp="${1}_v4_only.exp" + else + if [ "$3" = "prefer_v6" ]; then + exp="${1}_v4v6.exp" + else + exp="${1}_v4v6_prefer_v4.exp" + fi + fi + elif [ "$2" = "ifconfig" ]; then + lcl=$(ifconfig lo0 | grep inet6) + if [ -n "${lcl}" ]; then + if [ "$3" = "prefer_v6" ]; then + exp="${1}_v4v6.exp" + else + exp="${1}_v4v6_prefer_v4.exp" + fi + else + exp="${1}_v4_only.exp" + fi + else + atf_fail "Invalid family_match_type $2 requested." + fi + + cmp -s "$(atf_get_srcdir)/data/${exp}" out && return + diff -u "$(atf_get_srcdir)/data/${exp}" out || atf_fail "Actual output does not match expected output" +} + +atf_test_case basic_prefer_v4 cleanup +basic_prefer_v4_head() +{ + atf_set "descr" "Testing basic ones with prefer_v4" + atf_set "require.user" "root" +} +basic_prefer_v4_body() +{ + vnet_mkjail basic_prefer_v4 prefer_ipv4 + TEST="jexec getaddrinfo_test_basic_prefer_v4 $(atf_get_srcdir)/h_gai" + + ( $TEST ::1 http + $TEST 127.0.0.1 http + $TEST localhost http + $TEST ::1 tftp + $TEST 127.0.0.1 tftp + $TEST localhost tftp + $TEST ::1 echo + $TEST 127.0.0.1 echo + $TEST localhost echo ) > out 2>&1 + + check_output basics hosts prefer_v4 +} +basic_prefer_v4_cleanup() +{ + vnet_cleanup basic_prefer_v4 +} + +atf_test_case basic cleanup +basic_head() +{ + atf_set "descr" "Testing basic ones with prefer_v6" + atf_set "require.user" "root" +} +basic_body() +{ + vnet_mkjail basic prefer_ipv6 + TEST="jexec getaddrinfo_test_basic $(atf_get_srcdir)/h_gai" + + ( $TEST ::1 http + $TEST 127.0.0.1 http + $TEST localhost http + $TEST ::1 tftp + $TEST 127.0.0.1 tftp + $TEST localhost tftp + $TEST ::1 echo + $TEST 127.0.0.1 echo + $TEST localhost echo ) > out 2>&1 + + check_output basics ifconfig prefer_v6 +} +basic_cleanup() +{ + vnet_cleanup basic +} + +atf_test_case specific_prefer_v4 cleanup +specific_prefer_v4_head() +{ + atf_set "descr" "Testing specific address family with prefer_v4" + atf_set "require.user" "root" +} +specific_prefer_v4_body() +{ + vnet_mkjail specific_prefer_v4 prefer_ipv4 + TEST="jexec getaddrinfo_test_specific_prefer_v4 $(atf_get_srcdir)/h_gai" + + ( $TEST -4 localhost http + $TEST -6 localhost http ) > out 2>&1 + + check_output spec_fam hosts prefer_v4 +} +specific_prefer_v4_cleanup() +{ + vnet_cleanup specific_prefer_v4 +} + +atf_test_case specific cleanup +specific_head() +{ + atf_set "descr" "Testing specific address family with prefer_v6" + atf_set "require.user" "root" +} +specific_body() +{ + vnet_mkjail specific prefer_ipv6 + TEST="jexec getaddrinfo_test_specific $(atf_get_srcdir)/h_gai" + + ( $TEST -4 localhost http + $TEST -6 localhost http ) > out 2>&1 + + check_output spec_fam hosts prefer_v6 +} +specific_cleanup() +{ + vnet_cleanup specific +} + +atf_test_case empty_hostname_prefer_v4 cleanup +empty_hostname_prefer_v4_head() +{ + atf_set "descr" "Testing empty hostname with prefer_v4" + atf_set "require.user" "root" +} +empty_hostname_prefer_v4_body() +{ + vnet_mkjail empty_hostname_prefer_v4 prefer_ipv4 + TEST="jexec getaddrinfo_test_empty_hostname_prefer_v4 $(atf_get_srcdir)/h_gai" + + ( $TEST '' http + $TEST '' echo + $TEST '' tftp + $TEST '' 80 + $TEST -P '' http + $TEST -P '' echo + $TEST -P '' tftp + $TEST -P '' 80 + $TEST -S '' 80 + $TEST -D '' 80 ) > out 2>&1 + + check_output no_host ifconfig prefer_v4 +} +empty_hostname_prefer_v4_cleanup() +{ + vnet_cleanup empty_hostname_prefer_v4 +} + +atf_test_case empty_hostname cleanup +empty_hostname_head() +{ + atf_set "descr" "Testing empty hostname with prefer_v6" + atf_set "require.user" "root" +} +empty_hostname_body() +{ + vnet_mkjail empty_hostname prefer_ipv6 + TEST="jexec getaddrinfo_test_empty_hostname $(atf_get_srcdir)/h_gai" + + ( $TEST '' http + $TEST '' echo + $TEST '' tftp + $TEST '' 80 + $TEST -P '' http + $TEST -P '' echo + $TEST -P '' tftp + $TEST -P '' 80 + $TEST -S '' 80 + $TEST -D '' 80 ) > out 2>&1 + + check_output no_host ifconfig prefer_v6 +} +empty_hostname_cleanup() +{ + vnet_cleanup empty_hostname +} + +atf_test_case empty_servname_prefer_v4 cleanup +empty_servname_prefer_v4_head() +{ + atf_set "descr" "Testing empty service name with prefer_v4" + atf_set "require.user" "root" +} +empty_servname_prefer_v4_body() +{ + vnet_mkjail empty_servname_prefer_v4 prefer_ipv4 + TEST="jexec getaddrinfo_test_empty_servname_prefer_v4 $(atf_get_srcdir)/h_gai" + + ( $TEST ::1 '' + $TEST 127.0.0.1 '' + $TEST localhost '' + $TEST '' '' ) > out 2>&1 + + check_output no_serv hosts prefer_v4 +} +empty_servname_prefer_v4_cleanup() +{ + vnet_cleanup empty_servname_prefer_v4 +} + +atf_test_case empty_servname cleanup +empty_servname_head() +{ + atf_set "descr" "Testing empty service name with prefer_v6" + atf_set "require.user" "root" +} +empty_servname_body() +{ + vnet_mkjail empty_servname prefer_ipv6 + TEST="jexec getaddrinfo_test_empty_servname $(atf_get_srcdir)/h_gai" + + ( $TEST ::1 '' + $TEST 127.0.0.1 '' + $TEST localhost '' + $TEST '' '' ) > out 2>&1 + + check_output no_serv ifconfig prefer_v6 +} +empty_servname_cleanup() +{ + vnet_cleanup empty_servname +} + +atf_test_case sock_raw_prefer_v4 cleanup +sock_raw_prefer_v4_head() +{ + atf_set "descr" "Testing raw socket with prefer_v4" + atf_set "require.user" "root" +} +sock_raw_prefer_v4_body() +{ + vnet_mkjail sock_raw_prefer_v4 prefer_ipv4 + TEST="jexec getaddrinfo_test_sock_raw_prefer_v4 $(atf_get_srcdir)/h_gai" + + ( $TEST -R -p 0 localhost '' + $TEST -R -p 59 localhost '' + $TEST -R -p 59 localhost 80 + $TEST -R -p 59 localhost www + $TEST -R -p 59 ::1 '' ) > out 2>&1 + + check_output sock_raw hosts prefer_v4 +} +sock_raw_prefer_v4_cleanup() +{ + vnet_cleanup sock_raw_prefer_v4 +} + +atf_test_case sock_raw cleanup +sock_raw_head() +{ + atf_set "descr" "Testing raw socket with prefer_v6" + atf_set "require.user" "root" +} +sock_raw_body() +{ + vnet_mkjail sock_raw prefer_ipv6 + TEST="jexec getaddrinfo_test_sock_raw $(atf_get_srcdir)/h_gai" + + ( $TEST -R -p 0 localhost '' + $TEST -R -p 59 localhost '' + $TEST -R -p 59 localhost 80 + $TEST -R -p 59 localhost www + $TEST -R -p 59 ::1 '' ) > out 2>&1 + + check_output sock_raw ifconfig prefer_v6 +} +sock_raw_cleanup() +{ + vnet_cleanup sock_raw +} + +atf_test_case unsupported_family_prefer_v4 cleanup +unsupported_family_prefer_v4_head() +{ + atf_set "descr" "Testing unsupported family with prefer_v4" + atf_set "require.user" "root" +} +unsupported_family_prefer_v4_body() +{ + vnet_mkjail unsupported_family_prefer_v4 prefer_ipv4 + TEST="jexec getaddrinfo_test_unsupported_family_prefer_v4 $(atf_get_srcdir)/h_gai" + + ( $TEST -f 99 localhost '' ) > out 2>&1 + + check_output unsup_fam ifconfig prefer_v4 +} +unsupported_family_prefer_v4_cleanup() +{ + vnet_cleanup unsupported_family_prefer_v4 +} + +atf_test_case unsupported_family cleanup +unsupported_family_head() +{ + atf_set "descr" "Testing unsupported family with prefer_v6" + atf_set "require.user" "root" +} +unsupported_family_body() +{ + vnet_mkjail unsupported_family prefer_ipv6 + TEST="jexec getaddrinfo_test_unsupported_family $(atf_get_srcdir)/h_gai" + + ( $TEST -f 99 localhost '' ) > out 2>&1 + + check_output unsup_fam none prefer_v6 +} +unsupported_family_cleanup() +{ + vnet_cleanup unsupported_family +} + +atf_test_case scopeaddr_prefer_v4 cleanup +scopeaddr_prefer_v4_head() +{ + atf_set "descr" "Testing scoped address format with prefer_v4" + atf_set "require.user" "root" +} +scopeaddr_prefer_v4_body() +{ + vnet_mkjail scopeaddr_prefer_v4 prefer_ipv4 + TEST="jexec getaddrinfo_test_scopeaddr_prefer_v4 $(atf_get_srcdir)/h_gai" + + ( $TEST fe80::1%lo0 http +# IF=ifconfig -a | grep -v '^ ' | sed -e 's/:.*//' | head -1 | awk '{print $1}' +# $TEST fe80::1%$IF http + ) > out 2>&1 + + check_output scoped ifconfig prefer_v4 +} +scopeaddr_prefer_v4_cleanup() +{ + vnet_cleanup scopeaddr_prefer_v4 +} + +atf_test_case scopeaddr cleanup +scopeaddr_head() +{ + atf_set "descr" "Testing scoped address format with prefer_v6" + atf_set "require.user" "root" +} +scopeaddr_body() +{ + vnet_mkjail scopeaddr prefer_ipv6 + TEST="jexec getaddrinfo_test_scopeaddr $(atf_get_srcdir)/h_gai" + + ( $TEST fe80::1%lo0 http +# IF=ifconfig -a | grep -v '^ ' | sed -e 's/:.*//' | head -1 | awk '{print $1}' +# $TEST fe80::1%$IF http + ) > out 2>&1 + + check_output scoped none prefer_v6 +} +scopeaddr_cleanup() +{ + vnet_cleanup scopeaddr +} + +atf_init_test_cases() +{ + atf_add_test_case basic_prefer_v4 + atf_add_test_case specific_prefer_v4 + atf_add_test_case empty_hostname_prefer_v4 + atf_add_test_case empty_servname_prefer_v4 + atf_add_test_case sock_raw_prefer_v4 + atf_add_test_case unsupported_family_prefer_v4 + atf_add_test_case scopeaddr_prefer_v4 + + atf_add_test_case basic + atf_add_test_case specific + atf_add_test_case empty_hostname + atf_add_test_case empty_servname + atf_add_test_case sock_raw + atf_add_test_case unsupported_family + atf_add_test_case scopeaddr +} diff --git a/lib/libc/tests/net/inet_net_test.cc b/lib/libc/tests/net/inet_net_test.cc new file mode 100644 index 000000000000..60b60b152eca --- /dev/null +++ b/lib/libc/tests/net/inet_net_test.cc @@ -0,0 +1,333 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2025 Lexi Winter <ivy@FreeBSD.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Tests for inet_net_pton() and inet_net_ntop(). + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ranges> +#include <string> +#include <vector> + +#include <atf-c++.hpp> + +using namespace std::literals; + +/* + * inet_net_ntop() and inet_net_pton() for IPv4. + */ +ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet4) +ATF_TEST_CASE_BODY(inet_net_inet4) +{ + /* + * Define a list of addresses we want to check. Each address is passed + * to inet_net_pton() to convert it to an in_addr, then we convert the + * in_addr back to a string and compare it with the expected value. We + * want to test over-long prefixes here (such as 10.0.0.1/8), so we also + * specify what the result is expected to be. + */ + + struct test_addr { + std::string input; + int bits; + std::string output; + }; + + auto test_addrs = std::vector<test_addr>{ + // Simple prefixes that fall on octet boundaries. + { "10.0.0.0/8", 8, "10/8" }, + { "10.1.0.0/16", 16, "10.1/16" }, + { "10.1.2.0/24", 24, "10.1.2/24" }, + { "10.1.2.3/32", 32, "10.1.2.3/32" }, + + // Simple prefixes with the short-form address. + { "10/8", 8, "10/8" }, + { "10.1/16", 16, "10.1/16" }, + { "10.1.2/24", 24, "10.1.2/24" }, + + // A prefix that doesn't fall on an octet boundary. + { "10.1.64/18", 18, "10.1.64/18" }, + + // An overlong prefix with bits that aren't part of the prefix. + { "10.0.0.1/8", 8, "10/8" }, + }; + + for (auto const &addr: test_addrs) { + /* + * Convert the input string to an in_addr + bits, and make + * sure the result produces the number of bits we expected. + */ + + auto in = in_addr{}; + auto bits = inet_net_pton(AF_INET, addr.input.c_str(), + &in, sizeof(in)); + ATF_REQUIRE(bits != -1); + ATF_REQUIRE_EQ(bits, addr.bits); + + /* + * Convert the in_addr back to a string + */ + + /* + * XXX: Should there be a constant for the size of the result + * buffer? For now, use ADDRSTRLEN + 3 ("/32") + 1 (NUL). + * + * Fill the buffer with 'Z', so we can check the result was + * properly terminated. + */ + auto strbuf = std::vector<char>(INET_ADDRSTRLEN + 3 + 1, 'Z'); + auto ret = inet_net_ntop(AF_INET, &in, bits, + strbuf.data(), strbuf.size()); + ATF_REQUIRE(ret != NULL); + ATF_REQUIRE_EQ(ret, strbuf.data()); + + /* Make sure the result was NUL-terminated and find the NUL */ + ATF_REQUIRE(strbuf.size() >= 1); + auto end = std::ranges::find(strbuf, '\0'); + ATF_REQUIRE(end != strbuf.end()); + + /* + * Check the result matches what we expect. Use a temporary + * string here instead of std::ranges::equal because this + * means ATF can print the mismatch. + */ + auto str = std::string(std::ranges::begin(strbuf), end); + ATF_REQUIRE_EQ(str, addr.output); + } +} + +/* + * inet_net_ntop() and inet_net_pton() for IPv6. + */ +ATF_TEST_CASE_WITHOUT_HEAD(inet_net_inet6) +ATF_TEST_CASE_BODY(inet_net_inet6) +{ + /* + * Define a list of addresses we want to check. Each address is + * passed to inet_net_pton() to convert it to an in6_addr, then we + * convert the in6_addr back to a string and compare it with the + * expected value. We want to test over-long prefixes here (such + * as 2001:db8::1/32), so we also specify what the result is + * expected to be. + */ + + struct test_addr { + std::string input; + int bits; + std::string output; + }; + + auto test_addrs = std::vector<test_addr>{ + // A prefix with a trailing :: + { "2001:db8::/32", 32, "2001:db8::/32" }, + + // A prefix with a leading ::. Note that the output is + // different from the input because inet_ntop() renders + // this prefix with an IPv4 suffix for legacy reasons. + { "::ffff:0:0/96", 96, "::ffff:0.0.0.0/96" }, + + // The same prefix but with the IPv4 legacy form as input. + { "::ffff:0.0.0.0/96", 96, "::ffff:0.0.0.0/96" }, + + // A prefix with an infix ::. + { "2001:db8::1/128", 128, "2001:db8::1/128" }, + + // A prefix with bits set which are outside the prefix; + // these should be silently ignored. + { "2001:db8:1:1:1:1:1:1/32", 32, "2001:db8::/32" }, + + // As above but with infix ::. + { "2001:db8::1/32", 32, "2001:db8::/32" }, + + // A prefix with only ::, commonly used to represent the + // entire address space. + { "::/0", 0, "::/0" }, + + // A single address with no ::. + { "2001:db8:1:1:1:1:1:1/128", 128, "2001:db8:1:1:1:1:1:1/128" }, + + // A prefix with no ::. + { "2001:db8:1:1:0:0:0:0/64", 64, "2001:db8:1:1::/64" }, + + // A prefix which isn't on a 16-bit boundary. + { "2001:db8:c000::/56", 56, "2001:db8:c000::/56" }, + + // A prefix which isn't on a nibble boundary. + { "2001:db8:c100::/57", 57, "2001:db8:c100::/57" }, + + // An address without a prefix length, which should be treated + // as a /128. + { "2001:db8::", 128, "2001:db8::/128" }, + { "2001:db8::1", 128, "2001:db8::1/128" }, + + // Test vectors provided in PR bin/289198. + { "fe80::1/64", 64, "fe80::/64" }, + { "fe80::f000:74ff:fe54:bed2/64", + 64, "fe80::/64" }, + { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64", + 64, "ffff:ffff:ffff:ffff::/64" }, + { "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" }, + }; + + for (auto const &addr: test_addrs) { + /* + * Convert the input string to an in6_addr + bits, and make + * sure the result produces the number of bits we expected. + */ + + auto in6 = in6_addr{}; + errno = 0; + auto bits = inet_net_pton(AF_INET6, addr.input.c_str(), + &in6, sizeof(in6)); + ATF_REQUIRE(bits != -1); + ATF_REQUIRE_EQ(bits, addr.bits); + + /* + * Convert the in6_addr back to a string + */ + + /* + * XXX: Should there be a constant for the size of the result + * buffer? For now, use ADDRSTRLEN + 4 ("/128") + 1 (NUL). + * + * Fill the buffer with 'Z', so we can check the result was + * properly terminated. + */ + auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1, 'Z'); + auto ret = inet_net_ntop(AF_INET6, &in6, bits, + strbuf.data(), strbuf.size()); + ATF_REQUIRE(ret != NULL); + ATF_REQUIRE_EQ(ret, strbuf.data()); + + /* Make sure the result was NUL-terminated and find the NUL */ + ATF_REQUIRE(strbuf.size() >= 1); + auto end = std::ranges::find(strbuf, '\0'); + ATF_REQUIRE(end != strbuf.end()); + + /* + * Check the result matches what we expect. Use a temporary + * string here instead of std::ranges::equal because this + * means ATF can print the mismatch. + */ + auto str = std::string(std::ranges::begin(strbuf), end); + ATF_REQUIRE_EQ(str, addr.output); + } +} + +ATF_TEST_CASE_WITHOUT_HEAD(inet_net_pton_invalid) +ATF_TEST_CASE_BODY(inet_net_pton_invalid) +{ + auto ret = int{}; + auto addr4 = in_addr{}; + auto str4 = "10.0.0.0"s; + auto addr6 = in6_addr{}; + auto str6 = "2001:db8::"s; + + /* Passing an address which is too short should be an error */ + ret = inet_net_pton(AF_INET6, str6.c_str(), &addr6, sizeof(addr6) - 1); + ATF_REQUIRE_EQ(ret, -1); + + ret = inet_net_pton(AF_INET, str4.c_str(), &addr4, sizeof(addr4) - 1); + ATF_REQUIRE_EQ(ret, -1); + + /* Test some generally invalid addresses. */ + auto invalid4 = std::vector<std::string>{ + // Prefix length too big + "10.0.0.0/33", + // Prefix length is negative + "10.0.0.0/-1", + // Prefix length is not a number + "10.0.0.0/foo", + // Input is not a network prefix + "this is not an IP address", + }; + + for (auto const &addr: invalid4) { + auto ret = inet_net_pton(AF_INET, addr.c_str(), &addr4, + sizeof(addr4)); + ATF_REQUIRE_EQ(ret, -1); + } + + auto invalid6 = std::vector<std::string>{ + // Prefix length too big + "2001:db8::/129", + // Prefix length is negative + "2001:db8::/-1", + // Prefix length is not a number + "2001:db8::/foo", + // Input is not a network prefix + "this is not an IP address", + }; + + for (auto const &addr: invalid6) { + auto ret = inet_net_pton(AF_INET6, addr.c_str(), &addr6, + sizeof(addr6)); + ATF_REQUIRE_EQ(ret, -1); + } +} + +ATF_TEST_CASE_WITHOUT_HEAD(inet_net_ntop_invalid) +ATF_TEST_CASE_BODY(inet_net_ntop_invalid) +{ + auto addr4 = in_addr{}; + auto addr6 = in6_addr{}; + auto strbuf = std::vector<char>(INET6_ADDRSTRLEN + 4 + 1); + + /* + * Passing a buffer which is too small should not overrun the buffer. + * Test this by initialising the buffer to 'Z', and only providing + * part of it to the function. + */ + + std::ranges::fill(strbuf, 'Z'); + auto ret = inet_net_ntop(AF_INET6, &addr6, 128, strbuf.data(), 1); + ATF_REQUIRE_EQ(ret, nullptr); + ATF_REQUIRE_EQ(strbuf[1], 'Z'); + + std::ranges::fill(strbuf, 'Z'); + ret = inet_net_ntop(AF_INET, &addr4, 32, strbuf.data(), 1); + ATF_REQUIRE_EQ(ret, nullptr); + ATF_REQUIRE_EQ(strbuf[1], 'Z'); + + /* Check that invalid prefix lengths return an error */ + + ret = inet_net_ntop(AF_INET6, &addr6, 129, strbuf.data(), strbuf.size()); + ATF_REQUIRE_EQ(ret, nullptr); + ret = inet_net_ntop(AF_INET6, &addr6, -1, strbuf.data(), strbuf.size()); + ATF_REQUIRE_EQ(ret, nullptr); + + ret = inet_net_ntop(AF_INET, &addr4, 33, strbuf.data(), strbuf.size()); + ATF_REQUIRE_EQ(ret, nullptr); + ret = inet_net_ntop(AF_INET, &addr4, -1, strbuf.data(), strbuf.size()); + ATF_REQUIRE_EQ(ret, nullptr); +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, inet_net_inet4); + ATF_ADD_TEST_CASE(tcs, inet_net_inet6); + ATF_ADD_TEST_CASE(tcs, inet_net_pton_invalid); + ATF_ADD_TEST_CASE(tcs, inet_net_ntop_invalid); +} diff --git a/lib/libc/tests/net/link_addr_test.cc b/lib/libc/tests/net/link_addr_test.cc new file mode 100644 index 000000000000..b973b924dc13 --- /dev/null +++ b/lib/libc/tests/net/link_addr_test.cc @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2025 Lexi Winter + * + * SPDX-License-Identifier: ISC + */ + +/* + * Tests for link_addr() and link_ntoa(). + * + * link_addr converts a string representing an (optionally null) interface name + * followed by an Ethernet address into a sockaddr_dl. The expected format is + * "[ifname]:lladdr". This means if ifname is not specified, the leading colon + * is still required. + * + * link_ntoa does the inverse of link_addr, returning a string representation + * of the address. + * + * Note that the output format of link_ntoa is not valid input for link_addr + * since the leading colon may be omitted. This is by design. + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/ethernet.h> +#include <net/if_dl.h> + +#include <format> +#include <iostream> +#include <ranges> +#include <span> +#include <utility> +#include <vector> + +#include <cstddef> +#include <cstdint> + +#include <atf-c++.hpp> + +using namespace std::literals; + +/* + * Define operator== and operator<< for ether_addr so we can use them in + * ATF_EXPECT_EQ expressions. + */ + +bool +operator==(ether_addr a, ether_addr b) +{ + return (std::ranges::equal(a.octet, b.octet)); +} + +std::ostream & +operator<<(std::ostream &s, ether_addr a) +{ + for (unsigned i = 0; i < ETHER_ADDR_LEN; ++i) { + if (i > 0) + s << ":"; + + s << std::format("{:02x}", static_cast<int>(a.octet[i])); + } + + return (s); +} + +/* + * Create a sockaddr_dl from a string using link_addr(), and ensure the + * returned struct looks valid. + */ +sockaddr_dl +make_linkaddr(const std::string &addr) +{ + auto sdl = sockaddr_dl{}; + int ret; + + sdl.sdl_len = sizeof(sdl); + ret = ::link_addr(addr.c_str(), &sdl); + + ATF_REQUIRE_EQ(0, ret); + ATF_REQUIRE_EQ(AF_LINK, static_cast<int>(sdl.sdl_family)); + ATF_REQUIRE_EQ(true, sdl.sdl_len > 0); + ATF_REQUIRE_EQ(true, sdl.sdl_nlen >= 0); + + return (sdl); +} + +/* + * Return the data stored in a sockaddr_dl as a span. + */ +std::span<const char> +data(const sockaddr_dl &sdl) +{ + // sdl_len is the entire structure, but we only want the length of the + // data area. + auto dlen = sdl.sdl_len - offsetof(sockaddr_dl, sdl_data); + return {&sdl.sdl_data[0], dlen}; +} + +/* + * Return the interface name stored in a sockaddr_dl as a string. + */ +std::string_view +ifname(const sockaddr_dl &sdl) +{ + auto name = data(sdl).subspan(0, sdl.sdl_nlen); + return {name.begin(), name.end()}; +} + +/* + * Return the Ethernet address stored in a sockaddr_dl as an ether_addr. + */ +ether_addr +addr(const sockaddr_dl &sdl) +{ + ether_addr ret{}; + ATF_REQUIRE_EQ(ETHER_ADDR_LEN, sdl.sdl_alen); + std::ranges::copy(data(sdl).subspan(sdl.sdl_nlen, ETHER_ADDR_LEN), + &ret.octet[0]); + return (ret); +} + +/* + * Return the link address stored in a sockaddr_dl as a span of octets. + */ +std::span<const std::uint8_t> +lladdr(const sockaddr_dl &sdl) +{ + auto data = reinterpret_cast<const uint8_t *>(LLADDR(&sdl)); + return {data, data + sdl.sdl_alen}; +} + + +/* + * Some sample addresses we use for testing. Include at least one address for + * each format we want to support. + */ + +struct test_address { + std::string input; /* value passed to link_addr */ + std::string ntoa; /* expected return from link_ntoa */ + ether_addr addr{}; /* expected return from link_addr */ +}; + +std::vector<test_address> test_addresses{ + // No delimiter + {"001122334455"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Colon delimiter + {"00:11:22:33:44:55"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Dash delimiter + {"00-11-22-33-44-55"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Period delimiter (link_ntoa format) + {"00.11.22.33.44.55"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // Period delimiter (Cisco format) + {"0011.2233.4455"s, "0.11.22.33.44.55", + ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}}, + + // An addresses without leading zeroes. + {"0:1:02:30:4:55"s, "0.1.2.30.4.55", + ether_addr{0x00, 0x01, 0x02, 0x30, 0x04, 0x55}}, + + // An address with some uppercase letters. + {"AA:B:cC:Dd:e0:1f"s, "aa.b.cc.dd.e0.1f", + ether_addr{0xaa, 0x0b, 0xcc, 0xdd, 0xe0, 0x1f}}, + + // Addresses composed only of letters, to make sure they're not + // confused with an interface name. + + {"aabbccddeeff"s, "aa.bb.cc.dd.ee.ff", + ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, + + {"aa:bb:cc:dd:ee:ff"s, "aa.bb.cc.dd.ee.ff", + ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, + + // Address with a blank octet; this is an old form of Ethernet address. + {"00:11::33:44:55"s, "0.11.0.33.44.55", + ether_addr{0x00, 0x11, 0x00, 0x33, 0x44, 0x55}}, +}; + +/* + * Test without an interface name. + */ +ATF_TEST_CASE_WITHOUT_HEAD(basic) +ATF_TEST_CASE_BODY(basic) +{ + for (const auto &ta : test_addresses) { + // This does basic tests on the returned value. + auto sdl = make_linkaddr(":" + ta.input); + + // Check the ifname and address. + ATF_REQUIRE_EQ(""s, ifname(sdl)); + ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast<int>(sdl.sdl_alen)); + ATF_REQUIRE_EQ(ta.addr, addr(sdl)); + + // Check link_ntoa returns the expected value. + auto ntoa = std::string(::link_ntoa(&sdl)); + ATF_REQUIRE_EQ(ta.ntoa, ntoa); + } + +} + +/* + * Test with an interface name. + */ +ATF_TEST_CASE_WITHOUT_HEAD(ifname) +ATF_TEST_CASE_BODY(ifname) +{ + for (const auto &ta : test_addresses) { + auto sdl = make_linkaddr("ix0:" + ta.input); + + ATF_REQUIRE_EQ("ix0", ifname(sdl)); + ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast<int>(sdl.sdl_alen)); + ATF_REQUIRE_EQ(ta.addr, addr(sdl)); + + auto ntoa = std::string(::link_ntoa(&sdl)); + ATF_REQUIRE_EQ("ix0:" + ta.ntoa, ntoa); + } + +} + +/* + * Test with some invalid addresses. + */ +ATF_TEST_CASE_WITHOUT_HEAD(invalid) +ATF_TEST_CASE_BODY(invalid) +{ + auto const invalid_addresses = std::vector{ + // Invalid separator + ":1/2/3"s, + "ix0:1/2/3"s, + + // Multiple different separators + ":1.2-3"s, + "ix0:1.2-3"s, + + // An IP address + ":10.1.2.200/28"s, + "ix0:10.1.2.200/28"s, + + // Valid address followed by garbage + ":1.2.3xxx"s, + ":1.2.3.xxx"s, + "ix0:1.2.3xxx"s, + "ix0:1.2.3.xxx"s, + }; + + for (auto const &addr : invalid_addresses) { + int ret; + + auto sdl = sockaddr_dl{}; + sdl.sdl_len = sizeof(sdl); + + ret = link_addr(addr.c_str(), &sdl); + ATF_REQUIRE_EQ(-1, ret); + } +} + +/* + * Test some non-Ethernet addresses. + */ +ATF_TEST_CASE_WITHOUT_HEAD(nonether) +ATF_TEST_CASE_BODY(nonether) +{ + sockaddr_dl sdl; + + /* A short address */ + sdl = make_linkaddr(":1:23:cc"); + ATF_REQUIRE_EQ("", ifname(sdl)); + ATF_REQUIRE_EQ("1.23.cc"s, ::link_ntoa(&sdl)); + ATF_REQUIRE_EQ(3, sdl.sdl_alen); + ATF_REQUIRE_EQ(true, + std::ranges::equal(std::vector{0x01u, 0x23u, 0xccu}, lladdr(sdl))); + + /* A long address */ + sdl = make_linkaddr(":1:23:cc:a:b:c:d:e:f"); + ATF_REQUIRE_EQ("", ifname(sdl)); + ATF_REQUIRE_EQ("1.23.cc.a.b.c.d.e.f"s, ::link_ntoa(&sdl)); + ATF_REQUIRE_EQ(9, sdl.sdl_alen); + ATF_REQUIRE_EQ(true, std::ranges::equal( + std::vector{0x01u, 0x23u, 0xccu, 0xau, 0xbu, 0xcu, 0xdu, 0xeu, 0xfu}, + lladdr(sdl))); +} + +/* + * Test link_addr behaviour with undersized buffers. + */ +ATF_TEST_CASE_WITHOUT_HEAD(smallbuf) +ATF_TEST_CASE_BODY(smallbuf) +{ + static constexpr auto garbage = std::byte{0xcc}; + auto buf = std::vector<std::byte>(); + sockaddr_dl *sdl; + int ret; + + /* + * Make an sdl with an sdl_data member of the appropriate size, and + * place it in buf. Ensure it's followed by a trailing byte of garbage + * so we can test that link_addr doesn't write past the end. + */ + auto mksdl = [&buf](std::size_t datalen) -> sockaddr_dl * { + auto actual_size = datalen + offsetof(sockaddr_dl, sdl_data); + + buf.resize(actual_size + 1); + std::ranges::fill(buf, garbage); + + auto *sdl = new(reinterpret_cast<sockaddr_dl *>(&buf[0])) + sockaddr_dl; + sdl->sdl_len = actual_size; + return (sdl); + }; + + /* An sdl large enough to store the interface name */ + sdl = mksdl(3); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ(3, sdl->sdl_nlen); + ATF_REQUIRE_EQ("ix0", ifname(*sdl)); + ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen)); + + /* + * For these tests, test both with and without an interface name, since + * that will overflow the buffer in different places. + */ + + /* An empty sdl. Nothing may grow on this cursed ground. */ + + sdl = mksdl(0); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ(0, sdl->sdl_nlen); + ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen)); + + sdl = mksdl(0); + ret = link_addr(":1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ(0, sdl->sdl_nlen); + ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen)); + + /* + * An sdl large enough to store the interface name and two octets of the + * address. + */ + + sdl = mksdl(5); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ("ix0", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02 }, lladdr(*sdl))); + + sdl = mksdl(2); + ret = link_addr(":1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(ENOSPC, errno); + ATF_REQUIRE_EQ("", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02 }, lladdr(*sdl))); + + /* + * An sdl large enough to store the entire address. + */ + + sdl = mksdl(6); + ret = link_addr("ix0:1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(0, ret); + ATF_REQUIRE_EQ("ix0", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl))); + + sdl = mksdl(3); + ret = link_addr(":1.2.3", sdl); + ATF_REQUIRE(*rbegin(buf) == garbage); + ATF_REQUIRE_EQ(0, ret); + ATF_REQUIRE_EQ("", ifname(*sdl)); + ATF_REQUIRE(std::ranges::equal( + std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl))); +} + +/* + * Test an extremely long address which would overflow link_ntoa's internal + * buffer. It should handle this by truncating the output. + * (Test for SA-16:37.libc / CVE-2016-6559.) + */ +ATF_TEST_CASE_WITHOUT_HEAD(overlong) +ATF_TEST_CASE_BODY(overlong) +{ + auto sdl = make_linkaddr( + ":01.02.03.04.05.06.07.08.09.0a.0b.0c.0d.0e.0f." + "11.12.13.14.15.16.17.18.19.1a.1b.1c.1d.1e.1f." + "22.22.23.24.25.26.27.28.29.2a.2b.2c.2d.2e.2f"); + + ATF_REQUIRE_EQ( + "1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.11.12.13.14.15.16.17.18.19.1a.1b."s, + ::link_ntoa(&sdl)); +} + +/* + * Test link_ntoa_r, the re-entrant version of link_ntoa(). + */ +ATF_TEST_CASE_WITHOUT_HEAD(link_ntoa_r) +ATF_TEST_CASE_BODY(link_ntoa_r) +{ + static constexpr char garbage = 0x41; + std::vector<char> buf; + sockaddr_dl sdl; + size_t len; + int ret; + + // Return the contents of buf as a string, using the NUL terminator to + // determine length. This is to ensure we're using the return value in + // the same way C code would, but we do a bit more verification to + // elicit a test failure rather than a SEGV if it's broken. + auto bufstr = [&buf]() -> std::string_view { + // Find the NUL. + auto end = std::ranges::find(buf, '\0'); + ATF_REQUIRE(end != buf.end()); + + // Intentionally chopping the NUL off. + return {begin(buf), end}; + }; + + // Resize the buffer and set the contents to a known garbage value, so + // we don't accidentally have a NUL in the right place when link_ntoa_r + // didn't put it there. + auto resetbuf = [&buf, &len](std::size_t size) { + len = size; + buf.resize(len); + std::ranges::fill(buf, garbage); + }; + + // Test a short address with a large buffer. + sdl = make_linkaddr("ix0:1.2.3"); + resetbuf(64); + ret = ::link_ntoa_r(&sdl, &buf[0], &len); + ATF_REQUIRE_EQ(0, ret); + ATF_REQUIRE_EQ(10, len); + ATF_REQUIRE_EQ("ix0:1.2.3"s, bufstr()); + + // Test a buffer which is exactly the right size. + sdl = make_linkaddr("ix0:1.2.3"); + resetbuf(10); + ret = ::link_ntoa_r(&sdl, &buf[0], &len); + ATF_REQUIRE_EQ(0, ret); + ATF_REQUIRE_EQ(10, len); + ATF_REQUIRE_EQ("ix0:1.2.3"sv, bufstr()); + + // Test various short buffers, using a table of buffer length and the + // output we expect. All of these should produce valid but truncated + // strings, with a trailing NUL and with buflen set correctly. + + auto buftests = std::vector<std::pair<std::size_t, std::string_view>>{ + {1u, ""sv}, + {2u, ""sv}, + {3u, ""sv}, + {4u, "ix0"sv}, + {5u, "ix0:"sv}, + {6u, "ix0:1"sv}, + {7u, "ix0:1."sv}, + {8u, "ix0:1.2"sv}, + {9u, "ix0:1.2."sv}, + }; + + for (auto const &[buflen, expected] : buftests) { + sdl = make_linkaddr("ix0:1.2.3"); + resetbuf(buflen); + ret = ::link_ntoa_r(&sdl, &buf[0], &len); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(10, len); + ATF_REQUIRE_EQ(expected, bufstr()); + } + + // Test a NULL buffer, which should just set buflen. + sdl = make_linkaddr("ix0:1.2.3"); + len = 0; + ret = ::link_ntoa_r(&sdl, NULL, &len); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(10, len); + + // A NULL buffer with a non-zero length should also be accepted. + sdl = make_linkaddr("ix0:1.2.3"); + len = 64; + ret = ::link_ntoa_r(&sdl, NULL, &len); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(10, len); + + // Test a non-NULL buffer, but with a length of zero. + sdl = make_linkaddr("ix0:1.2.3"); + resetbuf(1); + len = 0; + ret = ::link_ntoa_r(&sdl, &buf[0], &len); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(10, len); + // Check we really didn't write anything. + ATF_REQUIRE_EQ(garbage, buf[0]); + + // Test a buffer which would be truncated in the middle of a two-digit + // hex octet, which should not write the truncated octet at all. + sdl = make_linkaddr("ix0:1.22.3"); + resetbuf(8); + ret = ::link_ntoa_r(&sdl, &buf[0], &len); + ATF_REQUIRE_EQ(-1, ret); + ATF_REQUIRE_EQ(11, len); + ATF_REQUIRE_EQ("ix0:1."sv, bufstr()); +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, basic); + ATF_ADD_TEST_CASE(tcs, ifname); + ATF_ADD_TEST_CASE(tcs, smallbuf); + ATF_ADD_TEST_CASE(tcs, invalid); + ATF_ADD_TEST_CASE(tcs, nonether); + ATF_ADD_TEST_CASE(tcs, overlong); + ATF_ADD_TEST_CASE(tcs, link_ntoa_r); +} diff --git a/lib/libc/tests/secure/Makefile b/lib/libc/tests/secure/Makefile index beaa01457cfe..515f8f53a43e 100644 --- a/lib/libc/tests/secure/Makefile +++ b/lib/libc/tests/secure/Makefile @@ -10,6 +10,7 @@ FORTIFY_TCATS+= uio # non-sys/ headers FORTIFY_TCATS+= poll +FORTIFY_TCATS+= signal FORTIFY_TCATS+= stdlib FORTIFY_TCATS+= stdio FORTIFY_TCATS+= string diff --git a/lib/libc/tests/secure/fortify_signal_test.c b/lib/libc/tests/secure/fortify_signal_test.c new file mode 100644 index 000000000000..03cfb9a9a13a --- /dev/null +++ b/lib/libc/tests/secure/fortify_signal_test.c @@ -0,0 +1,316 @@ +/* @generated by `generate-fortify-tests.lua "signal"` */ + +#define _FORTIFY_SOURCE 2 +#define TMPFILE_SIZE (1024 * 32) + +#include <sys/param.h> +#include <sys/jail.h> +#include <sys/random.h> +#include <sys/resource.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/uio.h> +#include <sys/wait.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <poll.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sysexits.h> +#include <unistd.h> +#include <wchar.h> +#include <atf-c.h> + +static FILE * __unused +new_fp(size_t __len) +{ + static char fpbuf[LINE_MAX]; + FILE *fp; + + ATF_REQUIRE(__len <= sizeof(fpbuf)); + + memset(fpbuf, 'A', sizeof(fpbuf) - 1); + fpbuf[sizeof(fpbuf) - 1] = '\0'; + + fp = fmemopen(fpbuf, sizeof(fpbuf), "rb"); + ATF_REQUIRE(fp != NULL); + + return (fp); +} + +/* + * Create a new symlink to use for readlink(2) style tests, we'll just use a + * random target name to have something interesting to look at. + */ +static const char * __unused +new_symlink(size_t __len) +{ + static const char linkname[] = "link"; + char target[MAXNAMLEN]; + int error; + + ATF_REQUIRE(__len <= sizeof(target)); + + arc4random_buf(target, sizeof(target)); + + error = unlink(linkname); + ATF_REQUIRE(error == 0 || errno == ENOENT); + + error = symlink(target, linkname); + ATF_REQUIRE(error == 0); + + return (linkname); +} + +/* + * For our purposes, first descriptor will be the reader; we'll send both + * raw data and a control message over it so that the result can be used for + * any of our recv*() tests. + */ +static void __unused +new_socket(int sock[2]) +{ + unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 }; + static char sockbuf[256]; + ssize_t rv; + size_t total = 0; + struct msghdr hdr = { 0 }; + struct cmsghdr *cmsg; + int error, fd; + + error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock); + ATF_REQUIRE(error == 0); + + while (total != sizeof(sockbuf)) { + rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0); + + ATF_REQUIRE_MSG(rv > 0, + "expected bytes sent, got %zd with %zu left (size %zu, total %zu)", + rv, sizeof(sockbuf) - total, sizeof(sockbuf), total); + ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf), + "%zd exceeds total %zu", rv, sizeof(sockbuf)); + total += rv; + } + + hdr.msg_control = ctrl; + hdr.msg_controllen = sizeof(ctrl); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + fd = STDIN_FILENO; + memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + error = sendmsg(sock[1], &hdr, 0); + ATF_REQUIRE(error != -1); +} + +/* + * Constructs a tmpfile that we can use for testing read(2) and friends. + */ +static int __unused +new_tmpfile(void) +{ + char buf[1024]; + ssize_t rv; + size_t written; + int fd; + + fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644); + ATF_REQUIRE(fd >= 0); + + written = 0; + while (written < TMPFILE_SIZE) { + rv = write(fd, buf, sizeof(buf)); + ATF_REQUIRE(rv > 0); + + written += rv; + } + + ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET)); + return (fd); +} + +static void +disable_coredumps(void) +{ + struct rlimit rl = { 0 }; + + if (setrlimit(RLIMIT_CORE, &rl) == -1) + _exit(EX_OSERR); +} + +/* + * Replaces stdin with a file that we can actually read from, for tests where + * we want a FILE * or fd that we can get data from. + */ +static void __unused +replace_stdin(void) +{ + int fd; + + fd = new_tmpfile(); + + (void)dup2(fd, STDIN_FILENO); + if (fd != STDIN_FILENO) + close(fd); +} + +ATF_TC(sig2str_before_end); +ATF_TC_HEAD(sig2str_before_end, tc) +{ +} +ATF_TC_BODY(sig2str_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[SIG2STR_MAX + 1]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = SIG2STR_MAX + 1; + const size_t __idx __unused = __len - 1; + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_end); +ATF_TC_HEAD(sig2str_end, tc) +{ +} +ATF_TC_BODY(sig2str_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[SIG2STR_MAX]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = SIG2STR_MAX; + const size_t __idx __unused = __len - 1; + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_heap_before_end); +ATF_TC_HEAD(sig2str_heap_before_end, tc) +{ +} +ATF_TC_BODY(sig2str_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX + 1); + const size_t __len = SIG2STR_MAX + 1; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_heap_end); +ATF_TC_HEAD(sig2str_heap_end, tc) +{ +} +ATF_TC_BODY(sig2str_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX); + const size_t __len = SIG2STR_MAX; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + sig2str(1, __stack.__buf); +#undef BUF + +} + +ATF_TC(sig2str_heap_after_end); +ATF_TC_HEAD(sig2str_heap_after_end, tc) +{ +} +ATF_TC_BODY(sig2str_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX - 1); + const size_t __len = SIG2STR_MAX - 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + + sig2str(1, __stack.__buf); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, sig2str_before_end); + ATF_TP_ADD_TC(tp, sig2str_end); + ATF_TP_ADD_TC(tp, sig2str_heap_before_end); + ATF_TP_ADD_TC(tp, sig2str_heap_end); + ATF_TP_ADD_TC(tp, sig2str_heap_after_end); + return (atf_no_error()); +} diff --git a/lib/libc/tests/secure/fortify_stdlib_test.c b/lib/libc/tests/secure/fortify_stdlib_test.c index ae021e8418f7..d0b1af78da86 100644 --- a/lib/libc/tests/secure/fortify_stdlib_test.c +++ b/lib/libc/tests/secure/fortify_stdlib_test.c @@ -305,6 +305,148 @@ monitor: } +ATF_TC(getenv_r_before_end); +ATF_TC_HEAD(getenv_r_before_end, tc) +{ +} +ATF_TC_BODY(getenv_r_before_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_end); +ATF_TC_HEAD(getenv_r_end, tc) +{ +} +ATF_TC_BODY(getenv_r_end, tc) +{ +#define BUF &__stack.__buf + struct { + uint8_t padding_l; + unsigned char __buf[42]; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(__stack.__buf); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_heap_before_end); +ATF_TC_HEAD(getenv_r_heap_before_end, tc) +{ +} +ATF_TC_BODY(getenv_r_heap_before_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 - 1; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_heap_end); +ATF_TC_HEAD(getenv_r_heap_end, tc) +{ +} +ATF_TC_BODY(getenv_r_heap_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42; + const size_t __idx __unused = __len - 1; + + __stack.__buf = malloc(__bufsz); + + getenv_r("PATH", __stack.__buf, __len); +#undef BUF + +} + +ATF_TC(getenv_r_heap_after_end); +ATF_TC_HEAD(getenv_r_heap_after_end, tc) +{ +} +ATF_TC_BODY(getenv_r_heap_after_end, tc) +{ +#define BUF __stack.__buf + struct { + uint8_t padding_l; + unsigned char * __buf; + uint8_t padding_r; + } __stack; + const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42); + const size_t __len = 42 + 1; + const size_t __idx __unused = __len - 1; + pid_t __child; + int __status; + + __child = fork(); + ATF_REQUIRE(__child >= 0); + if (__child > 0) + goto monitor; + + /* Child */ + disable_coredumps(); + __stack.__buf = malloc(__bufsz); + + getenv_r("PATH", __stack.__buf, __len); + _exit(EX_SOFTWARE); /* Should have aborted. */ + +monitor: + while (waitpid(__child, &__status, 0) != __child) { + ATF_REQUIRE_EQ(EINTR, errno); + } + + if (!WIFSIGNALED(__status)) { + switch (WEXITSTATUS(__status)) { + case EX_SOFTWARE: + atf_tc_fail("FORTIFY_SOURCE failed to abort"); + break; + case EX_OSERR: + atf_tc_fail("setrlimit(2) failed"); + break; + default: + atf_tc_fail("child exited with status %d", + WEXITSTATUS(__status)); + } + } else { + ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status)); + } +#undef BUF + +} + ATF_TC(realpath_before_end); ATF_TC_HEAD(realpath_before_end, tc) { @@ -454,6 +596,11 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, arc4random_buf_heap_before_end); ATF_TP_ADD_TC(tp, arc4random_buf_heap_end); ATF_TP_ADD_TC(tp, arc4random_buf_heap_after_end); + ATF_TP_ADD_TC(tp, getenv_r_before_end); + ATF_TP_ADD_TC(tp, getenv_r_end); + ATF_TP_ADD_TC(tp, getenv_r_heap_before_end); + ATF_TP_ADD_TC(tp, getenv_r_heap_end); + ATF_TP_ADD_TC(tp, getenv_r_heap_after_end); ATF_TP_ADD_TC(tp, realpath_before_end); ATF_TP_ADD_TC(tp, realpath_end); ATF_TP_ADD_TC(tp, realpath_heap_before_end); diff --git a/lib/libc/tests/secure/generate-fortify-tests.lua b/lib/libc/tests/secure/generate-fortify-tests.lua index 36ff01af7a17..c9cd9353a869 100755 --- a/lib/libc/tests/secure/generate-fortify-tests.lua +++ b/lib/libc/tests/secure/generate-fortify-tests.lua @@ -478,6 +478,18 @@ local all_tests = { init = poll_init, }, }, + signal = { + -- <signal.h> + { + func = "sig2str", + bufsize = "SIG2STR_MAX", + arguments = { + "1", + "__buf", + }, + exclude = excludes_stack_overflow, + }, + }, stdio = { -- <stdio.h> { @@ -585,6 +597,15 @@ local all_tests = { exclude = excludes_stack_overflow, }, { + func = "getenv_r", + arguments = { + "\"PATH\"", + "__buf", + "__len", + }, + exclude = excludes_stack_overflow, + }, + { func = "realpath", bufsize = "PATH_MAX", arguments = { diff --git a/lib/libc/tests/stdio/flushlbuf_test.c b/lib/libc/tests/stdio/flushlbuf_test.c index 11d9ea4ecc6c..3f8a6378f933 100644 --- a/lib/libc/tests/stdio/flushlbuf_test.c +++ b/lib/libc/tests/stdio/flushlbuf_test.c @@ -22,22 +22,23 @@ struct stream { unsigned int pos; }; -static int writefn(void *cookie, const char *buf, int len) +static int +writefn(void *cookie, const char *buf, int len) { struct stream *s = cookie; int written = 0; if (len <= 0) - return 0; + return (0); while (len > 0 && s->pos < s->len) { s->buf[s->pos++] = *buf++; written++; len--; } if (written > 0) - return written; + return (written); errno = EAGAIN; - return -1; + return (-1); } ATF_TC_WITHOUT_HEAD(flushlbuf_partial); @@ -147,7 +148,6 @@ ATF_TC_BODY(flushlbuf_full, tc) ATF_TP_ADD_TCS(tp) { - ATF_TP_ADD_TC(tp, flushlbuf_partial); ATF_TP_ADD_TC(tp, flushlbuf_full); diff --git a/lib/libc/tests/stdio/printfloat_test.c b/lib/libc/tests/stdio/printfloat_test.c index 031859124163..4493fe1c15d3 100644 --- a/lib/libc/tests/stdio/printfloat_test.c +++ b/lib/libc/tests/stdio/printfloat_test.c @@ -398,6 +398,18 @@ ATF_TC_BODY(subnormal_float, tc) testfmt("-0X1P-149", "%A", negative); } +ATF_TC_WITHOUT_HEAD(hexadecimal_rounding_fullprec); +ATF_TC_BODY(hexadecimal_rounding_fullprec, tc) +{ + /* Double: %.13a with binary64 mantissa=53 */ + testfmt("0x1.1234567890bbbp+0", "%.13a", 0x1.1234567890bbbp+0); + +#if defined(__aarch64__) + /* On arm64, long double is IEEE binary128 (mantissa=113) */ + testfmt("0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0", "%.28La", 0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0L); +#endif +} + ATF_TP_ADD_TCS(tp) { @@ -414,6 +426,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, hexadecimal_rounding); ATF_TP_ADD_TC(tp, subnormal_double); ATF_TP_ADD_TC(tp, subnormal_float); + ATF_TP_ADD_TC(tp, hexadecimal_rounding_fullprec); return (atf_no_error()); } diff --git a/lib/libc/tests/stdio/scanfloat_test.c b/lib/libc/tests/stdio/scanfloat_test.c index a93238504dd1..635d93d560b3 100644 --- a/lib/libc/tests/stdio/scanfloat_test.c +++ b/lib/libc/tests/stdio/scanfloat_test.c @@ -59,88 +59,88 @@ ATF_TC_BODY(normalized_numbers, tc) buf[0] = '\0'; ATF_REQUIRE(setlocale(LC_NUMERIC, "")); - sscanf("3.141592", "%e", &f); + ATF_REQUIRE_EQ(1, sscanf("3.141592", "%e", &f)); ATF_REQUIRE(eq(FLT, f, 3.141592)); - sscanf("3.141592653589793", "%lf", &d); + ATF_REQUIRE_EQ(1, sscanf("3.141592653589793", "%lf", &d)); ATF_REQUIRE(eq(DBL, d, 3.141592653589793)); - sscanf("1.234568e+06", "%E", &f); + ATF_REQUIRE_EQ(1, sscanf("1.234568e+06", "%E", &f)); ATF_REQUIRE(eq(FLT, f, 1.234568e+06)); - sscanf("-1.234568e6", "%lF", &d); + ATF_REQUIRE_EQ(1, sscanf("-1.234568e6", "%lF", &d)); ATF_REQUIRE(eq(DBL, d, -1.234568e6)); - sscanf("+1.234568e-52", "%LG", &ld); + ATF_REQUIRE_EQ(1, sscanf("+1.234568e-52", "%LG", &ld)); ATF_REQUIRE(eq(LDBL, ld, 1.234568e-52L)); - sscanf("0.1", "%la", &d); + ATF_REQUIRE_EQ(1, sscanf("0.1", "%la", &d)); ATF_REQUIRE(eq(DBL, d, 0.1)); - sscanf("00.2", "%lA", &d); + ATF_REQUIRE_EQ(1, sscanf("00.2", "%lA", &d)); ATF_REQUIRE(eq(DBL, d, 0.2)); - sscanf("123456", "%5le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("123456", "%5le%s", &d, buf)); ATF_REQUIRE(eq(DBL, d, 12345.)); ATF_REQUIRE(strcmp(buf, "6") == 0); - sscanf("1.0Q", "%*5le%s", buf); + ATF_REQUIRE_EQ(1, sscanf("1.0Q", "%*5le%s", buf)); ATF_REQUIRE(strcmp(buf, "Q") == 0); - sscanf("-1.23e", "%e%s", &f, buf); + ATF_REQUIRE_EQ(2, sscanf("-1.23e", "%e%s", &f, buf)); ATF_REQUIRE(eq(FLT, f, -1.23)); ATF_REQUIRE(strcmp(buf, "e") == 0); - sscanf("1.25e+", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("1.25e+", "%le%s", &d, buf)); ATF_REQUIRE(eq(DBL, d, 1.25)); ATF_REQUIRE(strcmp(buf, "e+") == 0); - sscanf("1.23E4E5", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("1.23E4E5", "%le%s", &d, buf)); ATF_REQUIRE(eq(DBL, d, 1.23e4)); ATF_REQUIRE(strcmp(buf, "E5") == 0); - sscanf("12e6", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("12e6", "%le", &d)); ATF_REQUIRE(eq(DBL, d, 12e6)); - sscanf("1.a", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("1.a", "%le%s", &d, buf)); ATF_REQUIRE(eq(DBL, d, 1.0)); ATF_REQUIRE(strcmp(buf, "a") == 0); - sscanf(".0p4", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf(".0p4", "%le%s", &d, buf)); ATF_REQUIRE(eq(DBL, d, 0.0)); ATF_REQUIRE(strcmp(buf, "p4") == 0); d = 0.25; - ATF_REQUIRE(sscanf(".", "%le", &d) == 0); + ATF_REQUIRE_EQ(0, sscanf(".", "%le", &d)); ATF_REQUIRE(d == 0.25); - sscanf("0x08", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x08", "%le", &d)); ATF_REQUIRE(d == 0x8p0); - sscanf("0x90a.bcdefP+09a", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("0x90a.bcdefP+09a", "%le%s", &d, buf)); ATF_REQUIRE(d == 0x90a.bcdefp+09); ATF_REQUIRE(strcmp(buf, "a") == 0); #if (LDBL_MANT_DIG > DBL_MANT_DIG) && !defined(__i386__) - sscanf("3.14159265358979323846", "%Lg", &ld); + ATF_REQUIRE_EQ(1, sscanf("3.14159265358979323846", "%Lg", &ld)); ATF_REQUIRE(eq(LDBL, ld, 3.14159265358979323846L)); - sscanf(" 0X.0123456789abcdefffp-3g", "%Le%s", &ld, buf); + ATF_REQUIRE_EQ(2, sscanf(" 0X.0123456789abcdefffp-3g", "%Le%s", &ld, buf)); ATF_REQUIRE(ld == 0x0.0123456789abcdefffp-3L); ATF_REQUIRE(strcmp(buf, "g") == 0); #endif - sscanf("0xg", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("0xg", "%le%s", &d, buf)); ATF_REQUIRE(d == 0.0); ATF_REQUIRE(strcmp(buf, "xg") == 0); ATF_REQUIRE(setlocale(LC_NUMERIC, "ru_RU.ISO8859-5")); /* decimalpoint==, */ - sscanf("1.23", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("1.23", "%le%s", &d, buf)); ATF_REQUIRE(d == 1.0); ATF_REQUIRE(strcmp(buf, ".23") == 0); - sscanf("1,23", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1,23", "%le", &d)); ATF_REQUIRE(d == 1.23); ATF_REQUIRE(setlocale(LC_NUMERIC, "")); @@ -156,40 +156,40 @@ ATF_TC_BODY(infinities_and_nans, tc) ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); - sscanf("-Inf", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("-Inf", "%le", &d)); ATF_REQUIRE(d < 0.0 && isinf(d)); - sscanf("iNfInItY and beyond", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("iNfInItY and beyond", "%le%s", &d, buf)); ATF_REQUIRE(d > 0.0 && isinf(d)); ATF_REQUIRE(strcmp(buf, " and beyond")); - sscanf("NaN", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("NaN", "%le", &d)); ATF_REQUIRE(isnan(d)); - sscanf("NAN(123Y", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("NAN(123Y", "%le%s", &d, buf)); ATF_REQUIRE(isnan(d)); ATF_REQUIRE(strcmp(buf, "(123Y") == 0); - sscanf("nan(f00f)plugh", "%le%s", &d, buf); + ATF_REQUIRE_EQ(2, sscanf("nan(f00f)plugh", "%le%s", &d, buf)); ATF_REQUIRE(isnan(d)); ATF_REQUIRE(strcmp(buf, "plugh") == 0); - sscanf("-nan", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("-nan", "%le", &d)); ATF_REQUIRE(isnan(d)); /* Only quiet NaNs should be returned. */ - sscanf("NaN", "%e", &f); - sscanf("nan", "%le", &d); - sscanf("nan", "%Le", &ld); - feclearexcept(FE_ALL_EXCEPT); + ATF_REQUIRE_EQ(1, sscanf("NaN", "%e", &f)); + ATF_REQUIRE_EQ(1, sscanf("nan", "%le", &d)); + ATF_REQUIRE_EQ(1, sscanf("nan", "%Le", &ld)); + ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT)); ATF_REQUIRE(f != f); ATF_REQUIRE(d != d); ATF_REQUIRE(ld != ld); ATF_REQUIRE(fetestexcept(FE_INVALID) == 0); - sscanf("nan(1234)", "%e", &f); - sscanf("nan(1234)", "%le", &d); - sscanf("nan(1234)", "%Le", &ld); - feclearexcept(FE_ALL_EXCEPT); + ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%e", &f)); + ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%le", &d)); + ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%Le", &ld)); + ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT)); ATF_REQUIRE(f != f); ATF_REQUIRE(d != d); ATF_REQUIRE(ld != ld); @@ -205,74 +205,74 @@ ATF_TC_BODY(rounding_tests, tc) ATF_REQUIRE(setlocale(LC_NUMERIC, "C")); - fesetround(FE_DOWNWARD); + ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD)); - sscanf("1.999999999999999999999999999999999", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.999999999999999999999999999999999", "%le", &d)); ATF_REQUIRE(d < 2.0); - sscanf("0x1.ffffffffffffffp0", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.ffffffffffffffp0", "%le", &d)); ATF_REQUIRE(d < 2.0); - sscanf("1.999999999999999999999999999999999", "%Le", &ld); + ATF_REQUIRE_EQ(1, sscanf("1.999999999999999999999999999999999", "%Le", &ld)); ATF_REQUIRE(ld < 2.0); - sscanf("1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0); - sscanf("-1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == -0x1.0ea3f4af0dc5ap0); - sscanf("1.0571892669084010", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0); - sscanf("0x1.23p-5000", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); ATF_REQUIRE(d == 0.0); - sscanf("0x1.2345678p-1050", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); ATF_REQUIRE(d == 0x1.234567p-1050); - fesetround(FE_UPWARD); + ATF_REQUIRE_EQ(0, fesetround(FE_UPWARD)); - sscanf("1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0); - sscanf("-1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0); - sscanf("1.0571892669084010", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc5bp0); - sscanf("0x1.23p-5000", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); ATF_REQUIRE(d == 0x1p-1074); - sscanf("0x1.2345678p-1050", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); ATF_REQUIRE(d == 0x1.234568p-1050); - fesetround(FE_TOWARDZERO); + ATF_REQUIRE_EQ(0, fesetround(FE_TOWARDZERO)); - sscanf("1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0); - sscanf("-1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0); - sscanf("1.0571892669084010", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0); - sscanf("0x1.23p-5000", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); ATF_REQUIRE(d == 0.0); - sscanf("0x1.2345678p-1050", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); ATF_REQUIRE(d == 0x1.234567p-1050); - fesetround(FE_TONEAREST); + ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST)); /* 1.0571892669084007 is slightly closer to 0x1.0ea3f4af0dc59p0 */ - sscanf("1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0); - sscanf("-1.0571892669084007", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d)); ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0); - sscanf("1.0571892669084010", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d)); ATF_REQUIRE(d == 0x1.0ea3f4af0dc5bp0); /* strtod() should round small numbers to 0. */ - sscanf("0x1.23p-5000", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d)); ATF_REQUIRE(d == 0.0); /* Extra digits in a denormal shouldn't break anything. */ - sscanf("0x1.2345678p-1050", "%le", &d); + ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d)); ATF_REQUIRE(d == 0x1.234568p-1050); } @@ -287,16 +287,16 @@ ATF_TC_BODY(strtod, tc) ATF_REQUIRE(strcmp("xy", endp) == 0); /* This used to cause an infinite loop and round the wrong way. */ - fesetround(FE_DOWNWARD); + ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD)); ATF_REQUIRE(strtof("3.5e38", &endp) == FLT_MAX); ATF_REQUIRE(strtod("2e308", &endp) == DBL_MAX); - fesetround(FE_UPWARD); + ATF_REQUIRE_EQ(0, fesetround(FE_UPWARD)); ATF_REQUIRE(strtof("3.5e38", &endp) == INFINITY); ATF_REQUIRE(strtod("2e308", &endp) == INFINITY); - fesetround(FE_TOWARDZERO); + ATF_REQUIRE_EQ(0, fesetround(FE_TOWARDZERO)); ATF_REQUIRE(strtof("3.5e38", &endp) == FLT_MAX); ATF_REQUIRE(strtod("2e308", &endp) == DBL_MAX); - fesetround(FE_TONEAREST); + ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST)); ATF_REQUIRE(strtof("3.5e38", &endp) == INFINITY); ATF_REQUIRE(strtod("2e308", &endp) == INFINITY); } diff --git a/lib/libc/tests/stdio/sscanf_test.c b/lib/libc/tests/stdio/sscanf_test.c index e916873d38c3..e43292820eeb 100644 --- a/lib/libc/tests/stdio/sscanf_test.c +++ b/lib/libc/tests/stdio/sscanf_test.c @@ -68,6 +68,31 @@ static const struct sscanf_test_case { { "0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, }, { "0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, }, { "0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + // all digits with two leading zeroes + { "000", { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, }, + { "001", { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, }, + { "002", { 1, 0, 2 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, }, + { "003", { 1, 0, 2 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, }, + { "004", { 1, 0, 2 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, }, + { "005", { 1, 0, 2 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, }, + { "006", { 1, 0, 2 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, }, + { "007", { 1, 0, 2 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, }, + { "008", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 8, 3 }, { 1, 8, 3 }, { 1, 0, 2 }, }, + { "009", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 9, 3 }, { 1, 9, 3 }, { 1, 0, 2 }, }, + { "00A", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { "00B", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { "00C", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { "00D", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { "00E", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { "00F", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { "00X", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + { "00a", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { "00b", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { "00c", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { "00d", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { "00e", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { "00f", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { "00x", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, // all digits with leading one { "10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, }, { "11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, }, diff --git a/lib/libc/tests/stdio/swscanf_test.c b/lib/libc/tests/stdio/swscanf_test.c index f7ad30b963a7..f0638081e16f 100644 --- a/lib/libc/tests/stdio/swscanf_test.c +++ b/lib/libc/tests/stdio/swscanf_test.c @@ -71,6 +71,31 @@ static const struct swscanf_test_case { { L"0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, }, { L"0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, }, { L"0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }, + // all digits with two leading zeroes + { L"000", { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, }, + { L"001", { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, }, + { L"002", { 1, 0, 2 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, }, + { L"003", { 1, 0, 2 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, }, + { L"004", { 1, 0, 2 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, }, + { L"005", { 1, 0, 2 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, }, + { L"006", { 1, 0, 2 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, }, + { L"007", { 1, 0, 2 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, }, + { L"008", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 8, 3 }, { 1, 8, 3 }, { 1, 0, 2 }, }, + { L"009", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 9, 3 }, { 1, 9, 3 }, { 1, 0, 2 }, }, + { L"00A", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { L"00B", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { L"00C", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { L"00D", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { L"00E", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { L"00F", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { L"00X", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, + { L"00a", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, }, + { L"00b", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, }, + { L"00c", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, }, + { L"00d", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, }, + { L"00e", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, }, + { L"00f", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, }, + { L"00x", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, }, // all digits with leading one { L"10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, }, { L"11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, }, diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile index 6f57b8014a1e..9d84becfbd1f 100644 --- a/lib/libc/tests/stdlib/Makefile +++ b/lib/libc/tests/stdlib/Makefile @@ -1,17 +1,20 @@ .include <src.opts.mk> ATF_TESTS_C+= clearenv_test +ATF_TESTS_C+= cxa_atexit_test ATF_TESTS_C+= dynthr_test +ATF_TESTS_C+= getenv_r_test ATF_TESTS_C+= heapsort_test ATF_TESTS_C+= libc_exit_test ATF_TESTS_C+= mergesort_test ATF_TESTS_C+= qsort_test -.if ${COMPILER_TYPE} == "clang" +.if ${COMPILER_FEATURES:Mblocks} ATF_TESTS_C+= qsort_b_test .endif ATF_TESTS_C+= qsort_r_compat_test ATF_TESTS_C+= qsort_r_test ATF_TESTS_C+= qsort_s_test +ATF_TESTS_C+= qsort_bench ATF_TESTS_C+= set_constraint_handler_s_test ATF_TESTS_C+= strfmon_test ATF_TESTS_C+= tsearch_test @@ -58,11 +61,9 @@ PROGS+= h_getopt h_getopt_long CFLAGS+= -I${.CURDIR} -CXXSTD.cxa_thread_atexit_test= c++11 -CXXSTD.cxa_thread_atexit_nothr_test= c++11 LIBADD.cxa_thread_atexit_test+= pthread -# Tests that requires Blocks feature +# Tests that require blocks support .for t in qsort_b_test CFLAGS.${t}.c+= -fblocks LIBADD.${t}+= BlocksRuntime @@ -79,5 +80,6 @@ LIBADD.libc_exit_test+= pthread LIBADD.strtod_test+= m SUBDIR+= dynthr_mod +SUBDIR+= libatexit .include <bsd.test.mk> diff --git a/lib/libc/tests/stdlib/cxa_atexit_test.c b/lib/libc/tests/stdlib/cxa_atexit_test.c new file mode 100644 index 000000000000..7e2cafbce850 --- /dev/null +++ b/lib/libc/tests/stdlib/cxa_atexit_test.c @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/wait.h> + +#include <dlfcn.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#include <atf-c.h> + +#define ARBITRARY_EXIT_CODE 42 + +static char * +get_shlib(const char *srcdir) +{ + char *shlib; + + shlib = NULL; + if (asprintf(&shlib, "%s/libatexit.so", srcdir) < 0) + atf_tc_fail("failed to construct path to libatexit.so"); + return (shlib); +} + +static void +run_test(const atf_tc_t *tc, bool with_fatal_atexit, bool with_exit) +{ + pid_t p; + void (*set_fatal_atexit)(bool); + void (*set_exit_code)(int); + void *hdl; + char *shlib; + + shlib = get_shlib(atf_tc_get_config_var(tc, "srcdir")); + + hdl = dlopen(shlib, RTLD_LAZY); + ATF_REQUIRE_MSG(hdl != NULL, "dlopen: %s", dlerror()); + + free(shlib); + + if (with_fatal_atexit) { + set_fatal_atexit = dlsym(hdl, "set_fatal_atexit"); + ATF_REQUIRE_MSG(set_fatal_atexit != NULL, + "set_fatal_atexit: %s", dlerror()); + } + if (with_exit) { + set_exit_code = dlsym(hdl, "set_exit_code"); + ATF_REQUIRE_MSG(set_exit_code != NULL, "set_exit_code: %s", + dlerror()); + } + + p = atf_utils_fork(); + if (p == 0) { + /* + * Don't let the child clobber the results file; stderr/stdout + * have been replaced by atf_utils_fork() to capture it. We're + * intentionally using exit() instead of _exit() here to run + * __cxa_finalize at exit, otherwise we'd just leave it be. + */ + closefrom(3); + + if (with_fatal_atexit) + set_fatal_atexit(true); + if (with_exit) + set_exit_code(ARBITRARY_EXIT_CODE); + + dlclose(hdl); + + /* + * If the dtor was supposed to exit (most cases), then we should + * not have made it to this point. If it's not supposed to + * exit, then we just exit with success here because we might + * be expecting either a clean exit or a signal on our way out + * as the final __cxa_finalize tries to run a callback in the + * unloaded DSO. + */ + if (with_exit) + exit(1); + exit(0); + } + + dlclose(hdl); + atf_utils_wait(p, with_exit ? ARBITRARY_EXIT_CODE : 0, "", ""); +} + +ATF_TC_WITHOUT_HEAD(simple_cxa_atexit); +ATF_TC_BODY(simple_cxa_atexit, tc) +{ + /* + * This test exits in a global object's dtor so that we check for our + * dtor being run at dlclose() time. If it isn't, then the forked child + * will have a chance to exit(1) after dlclose() to raise a failure. + */ + run_test(tc, false, true); +} + +ATF_TC_WITHOUT_HEAD(late_cxa_atexit); +ATF_TC_BODY(late_cxa_atexit, tc) +{ + /* + * This test creates another global object during a __cxa_atexit handler + * invocation. It's been observed in the wild that we weren't executing + * it, then the DSO gets torn down and it was executed at application + * exit time instead. In the best case scenario we would crash if + * something else hadn't been mapped there. + */ + run_test(tc, true, false); +} + +ATF_TC_WITHOUT_HEAD(late_cxa_atexit_ran); +ATF_TC_BODY(late_cxa_atexit_ran, tc) +{ + /* + * This is a slight variation of the previous test where we trigger an + * exit() in our late-registered __cxa_atexit handler so that we can + * ensure it was ran *before* dlclose() finished and not through some + * weird chain of events afterwards. + */ + run_test(tc, true, true); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, simple_cxa_atexit); + ATF_TP_ADD_TC(tp, late_cxa_atexit); + ATF_TP_ADD_TC(tp, late_cxa_atexit_ran); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdlib/getenv_r_test.c b/lib/libc/tests/stdlib/getenv_r_test.c new file mode 100644 index 000000000000..8085b92b1064 --- /dev/null +++ b/lib/libc/tests/stdlib/getenv_r_test.c @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +ATF_TC_WITHOUT_HEAD(getenv_r_ok); +ATF_TC_BODY(getenv_r_ok, tc) +{ + const char *ident = atf_tc_get_ident(tc); + char buf[256]; + + ATF_REQUIRE_EQ(0, setenv("ATF_TC_IDENT", ident, 1)); + ATF_REQUIRE_EQ(0, getenv_r("ATF_TC_IDENT", buf, sizeof(buf))); + ATF_REQUIRE_STREQ(ident, buf); +} + +ATF_TC_WITHOUT_HEAD(getenv_r_einval); +ATF_TC_BODY(getenv_r_einval, tc) +{ + char buf[256]; + + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r(NULL, buf, sizeof(buf))); + ATF_REQUIRE_EQ(EINVAL, errno); + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r("", buf, sizeof(buf))); + ATF_REQUIRE_EQ(EINVAL, errno); + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r("A=B", buf, sizeof(buf))); + ATF_REQUIRE_EQ(EINVAL, errno); +} + +ATF_TC_WITHOUT_HEAD(getenv_r_enoent); +ATF_TC_BODY(getenv_r_enoent, tc) +{ + char buf[256]; + + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r("no such variable", buf, sizeof(buf))); + ATF_REQUIRE_EQ(ENOENT, errno); +} + +ATF_TC_WITHOUT_HEAD(getenv_r_erange); +ATF_TC_BODY(getenv_r_erange, tc) +{ + const char *ident = atf_tc_get_ident(tc); + char buf[256]; + + ATF_REQUIRE_EQ(0, setenv("ATF_TC_IDENT", ident, 1)); + errno = 0; + ATF_REQUIRE_EQ(-1, getenv_r("ATF_TC_IDENT", buf, strlen(ident))); + ATF_REQUIRE_EQ(ERANGE, errno); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, getenv_r_ok); + ATF_TP_ADD_TC(tp, getenv_r_einval); + ATF_TP_ADD_TC(tp, getenv_r_enoent); + ATF_TP_ADD_TC(tp, getenv_r_erange); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdlib/libatexit/Makefile b/lib/libc/tests/stdlib/libatexit/Makefile new file mode 100644 index 000000000000..9ba04c77af62 --- /dev/null +++ b/lib/libc/tests/stdlib/libatexit/Makefile @@ -0,0 +1,11 @@ +SHLIB_CXX= libatexit +SHLIB_NAME= libatexit.so +SHLIB_MAJOR= 1 +SHLIBDIR= ${TESTSDIR} +PACKAGE= tests +SRCS= libatexit.cc + +TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/:H} + + +.include <bsd.lib.mk> diff --git a/lib/libc/tests/stdlib/libatexit/libatexit.cc b/lib/libc/tests/stdlib/libatexit/libatexit.cc new file mode 100644 index 000000000000..bb286c97e421 --- /dev/null +++ b/lib/libc/tests/stdlib/libatexit/libatexit.cc @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2025 Kyle Evans <kevans@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + * + */ + +#include <unistd.h> + +static int exit_code = -1; +static bool fatal_atexit; + +extern "C" { + void set_fatal_atexit(bool); + void set_exit_code(int); +} + +void +set_fatal_atexit(bool fexit) +{ + fatal_atexit = fexit; +} + +void +set_exit_code(int code) +{ + exit_code = code; +} + +struct other_object { + ~other_object() { + + /* + * In previous versions of our __cxa_atexit handling, we would + * never actually execute this handler because it's added during + * ~object() below; __cxa_finalize would never revisit it. We + * will allow the caller to configure us to exit with a certain + * exit code so that it can run us twice: once to ensure we + * don't crash at the end, and again to make sure the handler + * actually ran. + */ + if (exit_code != -1) + _exit(exit_code); + } +}; + +void +create_staticobj() +{ + static other_object obj; +} + +struct object { + ~object() { + /* + * If we're doing the fatal_atexit behavior (i.e., create an + * object that will add its own dtor for __cxa_finalize), then + * we don't exit here. + */ + if (fatal_atexit) + create_staticobj(); + else if (exit_code != -1) + _exit(exit_code); + } +}; + +static object obj; diff --git a/lib/libc/tests/stdlib/qsort_bench.c b/lib/libc/tests/stdlib/qsort_bench.c new file mode 100644 index 000000000000..5f2cfae40140 --- /dev/null +++ b/lib/libc/tests/stdlib/qsort_bench.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <atf-c.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +/*- + * Measures qsort(3) runtime with pathological input and verify that it + * stays close to N * log2(N). + * + * Thanks to Vivian Hussey for the proof of concept. + * + * The input we construct is similar to a sweep from 0 to N where each + * half, except for the first element, has been reversed; for instance, + * with N = 8, we get { 0, 3, 2, 1, 4, 8, 7, 6 }. This triggers a bug in + * the BSD qsort(3) where it will switch to insertion sort if the pivots + * are sorted. + * + * This article goes into more detail about the bug and its origin: + * + * https://www.raygard.net/2022/02/26/Re-engineering-a-qsort-part-3 + * + * With this optimization (the `if (swap_cnt == 0)` block), qsort(3) needs + * roughly N * N / 4 comparisons to sort our pathological input. Without + * it, it needs only a little more than N * log2(N) comparisons. + */ + +/* we stop testing once a single takes longer than this */ +#define MAXRUNSECS 10 + +static bool debugging; + +static uintmax_t ncmp; + +static int +intcmp(const void *a, const void *b) +{ + ncmp++; + return ((*(int *)a > *(int *)b) - (*(int *)a < *(int *)b)); +} + +static void +qsort_bench(int log2n) +{ + uintmax_t n = 1LLU << log2n; + int *buf; + + /* fill an array with a pathological pattern */ + ATF_REQUIRE(buf = malloc(n * sizeof(*buf))); + buf[0] = 0; + buf[n / 2] = n / 2; + for (unsigned int i = 1; i < n / 2; i++) { + buf[i] = n / 2 - i; + buf[n / 2 + i] = n - i; + } + + ncmp = 0; + qsort(buf, n, sizeof(*buf), intcmp); + + /* check result and free array */ + if (debugging) { + for (unsigned int i = 1; i < n; i++) { + ATF_REQUIRE_MSG(buf[i] > buf[i - 1], + "array is not sorted"); + } + } + free(buf); + + /* check that runtime does not exceed N² */ + ATF_CHECK_MSG(ncmp / n < n, + "runtime %ju exceeds N² for N = %ju", ncmp, n); + + /* check that runtime does not exceed N log N by much */ + ATF_CHECK_MSG(ncmp / n <= log2n + 1, + "runtime %ju exceeds N log N for N = %ju", ncmp, n); +} + +ATF_TC_WITHOUT_HEAD(qsort_bench); +ATF_TC_BODY(qsort_bench, tc) +{ + struct timespec t0, t1; + uintmax_t tus; + + for (int i = 10; i <= 30; i++) { + clock_gettime(CLOCK_UPTIME, &t0); + qsort_bench(i); + clock_gettime(CLOCK_UPTIME, &t1); + tus = t1.tv_sec * 1000000 + t1.tv_nsec / 1000; + tus -= t0.tv_sec * 1000000 + t0.tv_nsec / 1000; + if (debugging) { + fprintf(stderr, "N = 2^%d in %ju.%06jus\n", + i, tus / 1000000, tus % 1000000); + } + /* stop once an individual run exceeds our limit */ + if (tus / 1000000 >= MAXRUNSECS) + break; + } +} + +ATF_TP_ADD_TCS(tp) +{ + debugging = !getenv("__RUNNING_INSIDE_ATF_RUN") && + isatty(STDERR_FILENO); + ATF_TP_ADD_TC(tp, qsort_bench); + return (atf_no_error()); +} diff --git a/lib/libc/tests/stdtime/Makefile b/lib/libc/tests/stdtime/Makefile index c7a7f5b9436f..590dea22da31 100644 --- a/lib/libc/tests/stdtime/Makefile +++ b/lib/libc/tests/stdtime/Makefile @@ -1,6 +1,12 @@ -.include <bsd.own.mk> +.include <src.opts.mk> ATF_TESTS_C+= strptime_test +ATF_TESTS_C+= detect_tz_changes_test + +CFLAGS.detect_tz_changes_test+= -I${SRCTOP}/contrib/tzcode +.if ${MK_DETECT_TZ_CHANGES} != "no" +CFLAGS.detect_tz_changes_test+= -DDETECT_TZ_CHANGES +.endif TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/} diff --git a/lib/libc/tests/stdtime/detect_tz_changes_test.c b/lib/libc/tests/stdtime/detect_tz_changes_test.c new file mode 100644 index 000000000000..06c31c9fbc3d --- /dev/null +++ b/lib/libc/tests/stdtime/detect_tz_changes_test.c @@ -0,0 +1,477 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dlfcn.h> +#include <fcntl.h> +#include <limits.h> +#include <poll.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "tzdir.h" + +#include <atf-c.h> + +struct tzcase { + const char *tzfn; + const char *expect; +}; + +static const struct tzcase tzcases[] = { + /* + * A handful of time zones and the expected result of + * strftime("%z (%Z)", tm) when that time zone is active + * and tm represents a date in the summer of 2025. + */ + { "America/Vancouver", "-0700 (PDT)" }, + { "America/New_York", "-0400 (EDT)" }, + { "Europe/London", "+0100 (BST)" }, + { "Europe/Paris", "+0200 (CEST)" }, + { "Asia/Kolkata", "+0530 (IST)" }, + { "Asia/Tokyo", "+0900 (JST)" }, + { "Australia/Canberra", "+1000 (AEST)" }, + { "UTC", "+0000 (UTC)" }, + { 0 }, +}; +static const struct tzcase utc = { "UTC", "+0000 (UTC)" }; +static const struct tzcase invalid = { "invalid", "+0000 (-00)" }; +static const time_t then = 1751328000; /* 2025-07-01 00:00:00 UTC */ + +static bool debugging; + +static void +debug(const char *fmt, ...) +{ + va_list ap; + + if (debugging) { + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + } +} + +static void +change_tz(const char *tzn) +{ + static const char *zfn = TZDIR; + static const char *tfn = "root" TZDEFAULT ".tmp"; + static const char *dfn = "root" TZDEFAULT; + ssize_t clen; + int zfd, sfd, dfd; + + ATF_REQUIRE((zfd = open(zfn, O_DIRECTORY | O_SEARCH)) >= 0); + ATF_REQUIRE((sfd = openat(zfd, tzn, O_RDONLY)) >= 0); + ATF_REQUIRE((dfd = open(tfn, O_CREAT | O_TRUNC | O_WRONLY, 0644)) >= 0); + do { + clen = copy_file_range(sfd, NULL, dfd, NULL, SSIZE_MAX, 0); + ATF_REQUIRE_MSG(clen != -1, "failed to copy %s/%s: %m", + zfn, tzn); + } while (clen > 0); + ATF_CHECK_EQ(0, close(dfd)); + ATF_CHECK_EQ(0, close(sfd)); + ATF_CHECK_EQ(0, close(zfd)); + ATF_REQUIRE_EQ(0, rename(tfn, dfn)); + debug("time zone %s installed", tzn); +} + +static void +test_tz(const char *expect) +{ + char buf[128]; + struct tm *tm; + size_t len; + + ATF_REQUIRE((tm = localtime(&then)) != NULL); + len = strftime(buf, sizeof(buf), "%z (%Z)", tm); + ATF_REQUIRE(len > 0); + ATF_CHECK_STREQ(expect, buf); +} + +ATF_TC(tz_default); +ATF_TC_HEAD(tz_default, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test default zone"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(tz_default, tc) +{ + /* prepare chroot with no /etc/localtime */ + ATF_REQUIRE_EQ(0, mkdir("root", 0755)); + ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755)); + /* enter chroot */ + ATF_REQUIRE_EQ(0, chroot("root")); + ATF_REQUIRE_EQ(0, chdir("/")); + /* check timezone */ + unsetenv("TZ"); + test_tz("+0000 (UTC)"); +} + +ATF_TC(tz_invalid_file); +ATF_TC_HEAD(tz_invalid_file, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test invalid zone file"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(tz_invalid_file, tc) +{ + static const char *dfn = "root/etc/localtime"; + int fd; + + /* prepare chroot with bogus /etc/localtime */ + ATF_REQUIRE_EQ(0, mkdir("root", 0755)); + ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755)); + ATF_REQUIRE((fd = open(dfn, O_RDWR | O_CREAT, 0644)) >= 0); + ATF_REQUIRE_EQ(8, write(fd, "invalid\n", 8)); + ATF_REQUIRE_EQ(0, close(fd)); + /* enter chroot */ + ATF_REQUIRE_EQ(0, chroot("root")); + ATF_REQUIRE_EQ(0, chdir("/")); + /* check timezone */ + unsetenv("TZ"); + test_tz(invalid.expect); +} + +ATF_TC(thin_jail); +ATF_TC_HEAD(thin_jail, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test typical thin jail scenario"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(thin_jail, tc) +{ + const struct tzcase *tzcase = tzcases; + + /* prepare chroot */ + ATF_REQUIRE_EQ(0, mkdir("root", 0755)); + ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755)); + change_tz(tzcase->tzfn); + /* enter chroot */ + ATF_REQUIRE_EQ(0, chroot("root")); + ATF_REQUIRE_EQ(0, chdir("/")); + /* check timezone */ + unsetenv("TZ"); + test_tz(tzcase->expect); +} + +#ifdef DETECT_TZ_CHANGES +/* + * Test time zone change detection. + * + * The parent creates a chroot containing only /etc/localtime, initially + * set to UTC. It then forks a child which enters the chroot, repeatedly + * checks the current time zone, and prints it to stdout if it changes + * (including once on startup). Meanwhile, the parent waits for output + * from the child. Every time it receives a line of text from the child, + * it checks that it is as expected, then changes /etc/localtime within + * the chroot to the next case in the list. Once it reaches the end of + * the list, it closes a pipe to notify the child, which terminates. + * + * Note that ATF and / or Kyua may have set the timezone before the test + * case starts (even unintentionally). Therefore, we start the test only + * after we've received and discarded the first report from the child, + * which should come almost immediately on startup. + */ +static const char *tz_change_interval_sym = "__tz_change_interval"; +static int *tz_change_interval_p; +static const int tz_change_interval = 3; +static int tz_change_timeout = 90; + +ATF_TC(detect_tz_changes); +ATF_TC_HEAD(detect_tz_changes, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test timezone change detection"); + atf_tc_set_md_var(tc, "require.user", "root"); + atf_tc_set_md_var(tc, "timeout", "600"); +} +ATF_TC_BODY(detect_tz_changes, tc) +{ + char obuf[1024] = ""; + char ebuf[1024] = ""; + struct pollfd fds[3]; + int opd[2], epd[2], spd[2]; + time_t changed, now; + const struct tzcase *tzcase = NULL; + struct tm *tm; + size_t olen = 0, elen = 0; + ssize_t rlen; + long curoff = LONG_MIN; + pid_t pid; + int nfds, status; + + /* speed up the test if possible */ + tz_change_interval_p = dlsym(RTLD_SELF, tz_change_interval_sym); + if (tz_change_interval_p != NULL && + *tz_change_interval_p > tz_change_interval) { + debug("reducing detection interval from %d to %d", + *tz_change_interval_p, tz_change_interval); + *tz_change_interval_p = tz_change_interval; + tz_change_timeout = tz_change_interval * 3; + } + /* prepare chroot */ + ATF_REQUIRE_EQ(0, mkdir("root", 0755)); + ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755)); + change_tz("UTC"); + time(&changed); + /* output, error, sync pipes */ + if (pipe(opd) != 0 || pipe(epd) != 0 || pipe(spd) != 0) + atf_tc_fail("failed to pipe"); + /* fork child */ + if ((pid = fork()) < 0) + atf_tc_fail("failed to fork"); + if (pid == 0) { + /* child */ + dup2(opd[1], STDOUT_FILENO); + close(opd[0]); + close(opd[1]); + dup2(epd[1], STDERR_FILENO); + close(epd[0]); + close(epd[1]); + close(spd[0]); + unsetenv("TZ"); + ATF_REQUIRE_EQ(0, chroot("root")); + ATF_REQUIRE_EQ(0, chdir("/")); + fds[0].fd = spd[1]; + fds[0].events = POLLIN; + for (;;) { + ATF_REQUIRE(poll(fds, 1, 100) >= 0); + if (fds[0].revents & POLLHUP) { + /* parent closed sync pipe */ + _exit(0); + } + ATF_REQUIRE((tm = localtime(&then)) != NULL); + if (tm->tm_gmtoff == curoff) + continue; + olen = strftime(obuf, sizeof(obuf), "%z (%Z)", tm); + ATF_REQUIRE(olen > 0); + fprintf(stdout, "%s\n", obuf); + fflush(stdout); + curoff = tm->tm_gmtoff; + } + _exit(2); + } + /* parent */ + close(opd[1]); + close(epd[1]); + close(spd[1]); + /* receive output until child terminates */ + fds[0].fd = opd[0]; + fds[0].events = POLLIN; + fds[1].fd = epd[0]; + fds[1].events = POLLIN; + fds[2].fd = spd[0]; + fds[2].events = POLLIN; + nfds = 3; + for (;;) { + ATF_REQUIRE(poll(fds, 3, 1000) >= 0); + time(&now); + if (fds[0].revents & POLLIN && olen < sizeof(obuf)) { + rlen = read(opd[0], obuf + olen, sizeof(obuf) - olen); + ATF_REQUIRE(rlen >= 0); + olen += rlen; + } + if (olen > 0) { + ATF_REQUIRE_EQ('\n', obuf[olen - 1]); + obuf[--olen] = '\0'; + /* tzcase will be NULL at first */ + if (tzcase != NULL) { + debug("%s", obuf); + ATF_REQUIRE_STREQ(tzcase->expect, obuf); + debug("change to %s detected after %d s", + tzcase->tzfn, (int)(now - changed)); + if (tz_change_interval_p != NULL) { + ATF_CHECK((int)(now - changed) >= + *tz_change_interval_p - 1); + ATF_CHECK((int)(now - changed) <= + *tz_change_interval_p + 1); + } + } + olen = 0; + /* first / next test case */ + if (tzcase == NULL) + tzcase = tzcases; + else + tzcase++; + if (tzcase->tzfn == NULL) { + /* test is over */ + break; + } + change_tz(tzcase->tzfn); + changed = now; + } + if (fds[1].revents & POLLIN && elen < sizeof(ebuf)) { + rlen = read(epd[0], ebuf + elen, sizeof(ebuf) - elen); + ATF_REQUIRE(rlen >= 0); + elen += rlen; + } + if (elen > 0) { + ATF_REQUIRE_EQ(elen, fwrite(ebuf, 1, elen, stderr)); + elen = 0; + } + if (nfds > 2 && fds[2].revents & POLLHUP) { + /* child closed sync pipe */ + break; + } + /* + * The timeout for this test case is set to 10 minutes, + * because it can take that long to run with the default + * 61-second interval. However, each individual tzcase + * entry should not take much longer than the detection + * interval to test, so we can detect a problem long + * before Kyua terminates us. + */ + if ((now - changed) > tz_change_timeout) { + close(spd[0]); + if (tz_change_interval_p == NULL && + tzcase == tzcases) { + /* + * The most likely explanation in this + * case is that libc was built without + * time zone change detection. + */ + atf_tc_skip("time zone change detection " + "does not appear to be enabled"); + } + atf_tc_fail("timed out waiting for change to %s " + "to be detected", tzcase->tzfn); + } + } + close(opd[0]); + close(epd[0]); + close(spd[0]); /* this will wake up and terminate the child */ + if (olen > 0) + ATF_REQUIRE_EQ(olen, fwrite(obuf, 1, olen, stdout)); + if (elen > 0) + ATF_REQUIRE_EQ(elen, fwrite(ebuf, 1, elen, stderr)); + ATF_REQUIRE_EQ(pid, waitpid(pid, &status, 0)); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(0, WEXITSTATUS(status)); +} +#endif /* DETECT_TZ_CHANGES */ + +static void +test_tz_env(const char *tzval, const char *expect) +{ + setenv("TZ", tzval, 1); + test_tz(expect); +} + +static void +tz_env_common(void) +{ + char path[MAXPATHLEN]; + const struct tzcase *tzcase = tzcases; + int len; + + /* relative path */ + for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++) + test_tz_env(tzcase->tzfn, tzcase->expect); + /* absolute path */ + for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++) { + len = snprintf(path, sizeof(path), "%s/%s", TZDIR, tzcase->tzfn); + ATF_REQUIRE(len > 0 && (size_t)len < sizeof(path)); + test_tz_env(path, tzcase->expect); + } + /* absolute path with additional slashes */ + for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++) { + len = snprintf(path, sizeof(path), "%s/////%s", TZDIR, tzcase->tzfn); + ATF_REQUIRE(len > 0 && (size_t)len < sizeof(path)); + test_tz_env(path, tzcase->expect); + } +} + +ATF_TC(tz_env); +ATF_TC_HEAD(tz_env, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test TZ environment variable"); +} +ATF_TC_BODY(tz_env, tc) +{ + tz_env_common(); + /* escape from TZDIR is permitted when not setugid */ + test_tz_env("../zoneinfo/UTC", utc.expect); +} + + +ATF_TC(tz_invalid_env); +ATF_TC_HEAD(tz_invalid_env, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test invalid TZ value"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(tz_invalid_env, tc) +{ + test_tz_env("invalid", invalid.expect); + test_tz_env(":invalid", invalid.expect); +} + +ATF_TC(setugid); +ATF_TC_HEAD(setugid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test setugid process"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(setugid, tc) +{ + const struct tzcase *tzcase = tzcases; + + /* prepare chroot */ + ATF_REQUIRE_EQ(0, mkdir("root", 0755)); + ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755)); + change_tz(tzcase->tzfn); + /* enter chroot */ + ATF_REQUIRE_EQ(0, chroot("root")); + ATF_REQUIRE_EQ(0, chdir("/")); + /* become setugid */ + ATF_REQUIRE_EQ(0, seteuid(UID_NOBODY)); + ATF_REQUIRE(issetugid()); + /* check timezone */ + unsetenv("TZ"); + test_tz(tzcases->expect); +} + +ATF_TC(tz_env_setugid); +ATF_TC_HEAD(tz_env_setugid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test TZ environment variable " + "in setugid process"); + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(tz_env_setugid, tc) +{ + ATF_REQUIRE_EQ(0, seteuid(UID_NOBODY)); + ATF_REQUIRE(issetugid()); + tz_env_common(); + /* escape from TZDIR is not permitted when setugid */ + test_tz_env("../zoneinfo/UTC", invalid.expect); +} + +ATF_TP_ADD_TCS(tp) +{ + debugging = !getenv("__RUNNING_INSIDE_ATF_RUN") && + isatty(STDERR_FILENO); + ATF_TP_ADD_TC(tp, tz_default); + ATF_TP_ADD_TC(tp, tz_invalid_file); + ATF_TP_ADD_TC(tp, thin_jail); +#ifdef DETECT_TZ_CHANGES + ATF_TP_ADD_TC(tp, detect_tz_changes); +#endif /* DETECT_TZ_CHANGES */ + ATF_TP_ADD_TC(tp, tz_env); + ATF_TP_ADD_TC(tp, tz_invalid_env); + ATF_TP_ADD_TC(tp, setugid); + ATF_TP_ADD_TC(tp, tz_env_setugid); + return (atf_no_error()); +} diff --git a/lib/libc/tests/string/memcmp_test.c b/lib/libc/tests/string/memcmp_test.c index 5286a0b994f3..fa2f498ccfaf 100644 --- a/lib/libc/tests/string/memcmp_test.c +++ b/lib/libc/tests/string/memcmp_test.c @@ -41,14 +41,14 @@ #endif /* - * On FreeBSD we demand that memcmp returns the difference between the - * characters at the first site of mismatch. However, ISO/IEC 9899:1990 - * only specifies that a number greater than, equal to, or less than - * zero shall be returned. If a unit test for this less strict - * behaviour is desired, define RES(x) to be (((x) > 0) - ((x) < 0)). + * On FreeBSD we previously demanded that memcmp returns the difference + * between the characters at the first site of mismatch. However, + * ISO/IEC 9899:1990 only specifies that a number greater than, equal + * to, or less than zero shall be returned. If a unit test for the + * more strict behaviour is desired, define RES(x) to be (x). */ #ifndef RES -#define RES(x) (x) +#define RES(x) (((x) > 0) - ((x) < 0)) #endif static int (*memcmp_fn)(const void *, const void *, size_t); diff --git a/lib/libc/tests/sys/Makefile b/lib/libc/tests/sys/Makefile index 89d341ff400a..88f8191a16eb 100644 --- a/lib/libc/tests/sys/Makefile +++ b/lib/libc/tests/sys/Makefile @@ -7,11 +7,11 @@ ATF_TESTS_C+= brk_test .endif ATF_TESTS_C+= cpuset_test ATF_TESTS_C+= errno_test +ATF_TESTS_C+= swapcontext_test ATF_TESTS_C+= queue_test ATF_TESTS_C+= sendfile_test -# TODO: clone, lwp_create, lwp_ctl, posix_fadvise, recvmmsg, -# swapcontext +# TODO: clone, lwp_create, lwp_ctl, posix_fadvise, recvmmsg NETBSD_ATF_TESTS_C+= access_test NETBSD_ATF_TESTS_C+= bind_test NETBSD_ATF_TESTS_C+= chroot_test diff --git a/lib/libc/tests/sys/swapcontext_test.c b/lib/libc/tests/sys/swapcontext_test.c new file mode 100644 index 000000000000..f341a746e515 --- /dev/null +++ b/lib/libc/tests/sys/swapcontext_test.c @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 2025 Raptor Computing Systems, LLC + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ucontext.h> +#include <errno.h> + +#include <atf-c.h> + +#define STACK_SIZE (64ull << 10) + +static volatile int callback_reached = 0; + +static ucontext_t uctx_save, uctx_switch; + +static void swapcontext_callback() +{ + // Increment callback reached variable + // If this is called multiple times, we will fail the test + // If this is not called at all, we will fail the test + callback_reached++; +} + +ATF_TC(swapcontext_basic); +ATF_TC_HEAD(swapcontext_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify basic functionality of swapcontext"); +} + +ATF_TC_BODY(swapcontext_basic, tc) +{ + char *stack; + int res; + + stack = malloc(STACK_SIZE); + ATF_REQUIRE_MSG(stack != NULL, "malloc failed: %s", strerror(errno)); + res = getcontext(&uctx_switch); + ATF_REQUIRE_MSG(res == 0, "getcontext failed: %s", strerror(errno)); + + uctx_switch.uc_stack.ss_sp = stack; + uctx_switch.uc_stack.ss_size = STACK_SIZE; + uctx_switch.uc_link = &uctx_save; + makecontext(&uctx_switch, swapcontext_callback, 0); + + res = swapcontext(&uctx_save, &uctx_switch); + + ATF_REQUIRE_MSG(res == 0, "swapcontext failed: %s", strerror(errno)); + ATF_REQUIRE_MSG(callback_reached == 1, + "callback failed, reached %d times", callback_reached); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, swapcontext_basic); + + return (atf_no_error()); +} + diff --git a/lib/libc/tests/tls/dso/Makefile b/lib/libc/tests/tls/dso/Makefile index 5efd8b29a6bd..783534ff7aae 100644 --- a/lib/libc/tests/tls/dso/Makefile +++ b/lib/libc/tests/tls/dso/Makefile @@ -6,6 +6,7 @@ SRCS= h_tls_dlopen.c MAN= PACKAGE= tests +NO_DEV_PACKAGE= LIBDIR= ${TESTSBASE}/lib/libc/tls SHLIB_MAJOR= 1 diff --git a/lib/libc/tests/tls_dso/Makefile b/lib/libc/tests/tls_dso/Makefile index 89296c643695..7cb8f98b431e 100644 --- a/lib/libc/tests/tls_dso/Makefile +++ b/lib/libc/tests/tls_dso/Makefile @@ -7,6 +7,7 @@ LIBDIR= ${TESTSBASE}/lib/libc/tls SHLIBDIR= ${TESTSBASE}/lib/libc/tls SHLIB_MAJOR= 1 PACKAGE= tests +NO_DEV_PACKAGE= WITHOUT_STATIC= WITHOUT_PROFILE= |