summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@fingolfin.org>2021-08-04 11:04:00 -0700
committerRobert Mustacchi <rm@fingolfin.org>2021-08-20 08:14:13 -0700
commitd6bf170859287b7b5b26419e266a7e6fdee7ff4d (patch)
treef91105a1d175639329cbacdaf0f981eeaca7d4be
parent4772ec9f4bc326d705888dcda49bcc395df74999 (diff)
downloadillumos-joyent-d6bf170859287b7b5b26419e266a7e6fdee7ff4d.tar.gz
13997 Want memrchr in libc
Reviewed by: Joshua M. Clulow <josh@sysmgr.org> Reviewed by: Andy Fiddaman <andy@omnios.org> Reviewed by: Toomas Soome <tsoome@me.com> Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r--usr/src/head/string.h3
-rw-r--r--usr/src/lib/libc/amd64/Makefile1
-rw-r--r--usr/src/lib/libc/i386/Makefile.com1
-rw-r--r--usr/src/lib/libc/port/gen/memrchr.c38
-rw-r--r--usr/src/lib/libc/port/mapfile-vers5
-rw-r--r--usr/src/lib/libc/sparc/Makefile.com1
-rw-r--r--usr/src/lib/libc/sparcv9/Makefile.com1
-rw-r--r--usr/src/man/man3c/Makefile2
-rw-r--r--usr/src/man/man3c/memory.3c381
-rw-r--r--usr/src/pkg/manifests/system-library.man3c.inc1
-rw-r--r--usr/src/pkg/manifests/system-test-libctest.mf2
-rw-r--r--usr/src/test/libc-tests/cfg/symbols/string_h.cfg8
-rw-r--r--usr/src/test/libc-tests/runfiles/default.run2
-rw-r--r--usr/src/test/libc-tests/tests/Makefile1
-rw-r--r--usr/src/test/libc-tests/tests/memchr.c294
15 files changed, 604 insertions, 137 deletions
diff --git a/usr/src/head/string.h b/usr/src/head/string.h
index f75c14c59c..f7d90e827e 100644
--- a/usr/src/head/string.h
+++ b/usr/src/head/string.h
@@ -121,6 +121,7 @@ extern int fls(int);
extern int flsl(long);
extern int flsll(long long);
extern void *memmem(const void *, size_t, const void *, size_t);
+extern void *memrchr(const void *, int, size_t);
extern char *strcasestr(const char *, const char *);
extern char *strnstr(const char *, const char *, size_t);
extern size_t strlcpy(char *, const char *, size_t);
@@ -130,7 +131,7 @@ extern char *strchrnul(const char *, int);
extern char *strcasestr_l(const char *, const char *, locale_t);
extern int strcasecmp(const char *, const char *);
extern int strncasecmp(const char *, const char *, size_t);
-#endif /* defined(__EXTENSIONS__)... */
+#endif /* !defined(__STRICT_SYMBOLS) */
#if defined(__EXTENSIONS__) || \
(!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \
diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile
index 0e2d78a484..0552156c53 100644
--- a/usr/src/lib/libc/amd64/Makefile
+++ b/usr/src/lib/libc/amd64/Makefile
@@ -470,6 +470,7 @@ PORTGEN= \
madvise.o \
malloc.o \
memalign.o \
+ memrchr.o \
memset_s.o \
mkdev.o \
mkdtemp.o \
diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com
index 55bee236c2..0b8a89f1e9 100644
--- a/usr/src/lib/libc/i386/Makefile.com
+++ b/usr/src/lib/libc/i386/Makefile.com
@@ -506,6 +506,7 @@ PORTGEN= \
madvise.o \
malloc.o \
memalign.o \
+ memrchr.o \
memset_s.o \
mkdev.o \
mkdtemp.o \
diff --git a/usr/src/lib/libc/port/gen/memrchr.c b/usr/src/lib/libc/port/gen/memrchr.c
new file mode 100644
index 0000000000..f7a8bbc9ab
--- /dev/null
+++ b/usr/src/lib/libc/port/gen/memrchr.c
@@ -0,0 +1,38 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2021 Oxide Computer Company
+ */
+
+/*
+ * memrchr(3C) implementation. Find the first occurence of 'c' as an unsigned
+ * char in 's', by searching in reverse.
+ */
+
+#include <string.h>
+
+void *
+memrchr(const void *s, int c, size_t len)
+{
+ unsigned char val = (unsigned char)c;
+ const unsigned char *data = s;
+
+ for (; len > 0; len--) {
+ size_t pos = len - 1;
+
+ if (data[pos] == val) {
+ return ((void *)&data[pos]);
+ }
+ }
+
+ return (NULL);
+}
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index 00d50029e0..8b9a2f170b 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -78,6 +78,11 @@ $if _x86 && _ELF64
$add amd64
$endif
+SYMBOL_VERSION ILLUMOS_0.39 {
+ protected:
+ memrchr;
+} ILLUMOS_0.38;
+
SYMBOL_VERSION ILLUMOS_0.38 {
protected:
getgrouplist;
diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com
index 9a8ca9ce52..e19a336f48 100644
--- a/usr/src/lib/libc/sparc/Makefile.com
+++ b/usr/src/lib/libc/sparc/Makefile.com
@@ -533,6 +533,7 @@ PORTGEN= \
madvise.o \
malloc.o \
memalign.o \
+ memrchr.o \
memset_s.o \
mkdev.o \
mkdtemp.o \
diff --git a/usr/src/lib/libc/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com
index 1058e950c9..c7a662d34e 100644
--- a/usr/src/lib/libc/sparcv9/Makefile.com
+++ b/usr/src/lib/libc/sparcv9/Makefile.com
@@ -491,6 +491,7 @@ PORTGEN= \
madvise.o \
malloc.o \
memalign.o \
+ memrchr.o \
memset_s.o \
mkdev.o \
mkdtemp.o \
diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile
index 33b572df61..0450a46eeb 100644
--- a/usr/src/man/man3c/Makefile
+++ b/usr/src/man/man3c/Makefile
@@ -1026,6 +1026,7 @@ MANLINKS= FD_CLR.3c \
memcpy.3c \
memmem.3c \
memmove.3c \
+ memrchr.3c \
memset.3c \
minor.3c \
mkdtemp.3c \
@@ -2046,6 +2047,7 @@ memcmp.3c := LINKSRC = memory.3c
memcpy.3c := LINKSRC = memory.3c
memmem.3c := LINKSRC = memory.3c
memmove.3c := LINKSRC = memory.3c
+memrchr.3c := LINKSRC = memory.3c
memset.3c := LINKSRC = memory.3c
mkfifoat.3c := LINKSRC = mkfifo.3c
diff --git a/usr/src/man/man3c/memory.3c b/usr/src/man/man3c/memory.3c
index 368956c97f..5a11990914 100644
--- a/usr/src/man/man3c/memory.3c
+++ b/usr/src/man/man3c/memory.3c
@@ -1,144 +1,253 @@
-'\" te
+.\"
.\" Copyright (c) 2014, Joyent, Inc.
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved.
.\" Copyright 1989 AT&T
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH MEMORY 3C "Feb 4, 2009"
-.SH NAME
-memory, memccpy, memchr, memcmp, memcpy, memmem, memmove, memset \- memory operations
-.SH SYNOPSIS
-.LP
-.nf
-#include <string.h>
-
-\fBvoid *\fR\fBmemccpy\fR(\fBvoid *restrict\fR \fIs1\fR, \fBconst void *restrict\fR \fIs2\fR,
- \fBint\fR \fIc\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.LP
-.nf
-\fBvoid *\fR\fBmemchr\fR(\fBconst void *\fR\fIs\fR, \fBint\fR \fIc\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.LP
-.nf
-\fBint\fR \fBmemcmp\fR(\fBconst void *\fR\fIs1\fR, \fBconst void *\fR\fIs2\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.LP
-.nf
-\fBvoid *\fR\fBmemcpy\fR(\fBvoid *restrict\fR \fIs1\fR, \fBconst void *restrict\fR \fIs2\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.LP
-.nf
-\fBvoid *\fR\fBmemmem\fR(\fBconst void *\fR\fIl\fR, \fBsize_t\fR \fIl_len\fR, \fBconst void *\fR\fIs\fR, \fBsize_t\fR \fIs_len\fR);
-.fi
-
-.LP
-.nf
-\fBvoid *\fR\fBmemmove\fR(\fBvoid *\fR\fIs1\fR, \fBconst void *\fR\fIs2\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.LP
-.nf
-\fBvoid *\fR\fBmemset\fR(\fBvoid *\fR\fIs\fR, \fBint\fR \fIc\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.SS "ISO C++"
-.LP
-.nf
-#include <string.h>
-
-\fBconst void *\fR\fBmemchr\fR(\fBconst void *\fR\fIs\fR, \fBint\fR \fIc\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.LP
-.nf
-#include <cstring>
-
-\fBvoid *std::\fR\fBmemchr\fR(\fBvoid *\fR\fIs\fR, \fBint\fR \fIc\fR, \fBsize_t\fR \fIn\fR);
-.fi
-
-.SH DESCRIPTION
-.LP
+.\" Copyright 2021 Oxide Computer Company
+.\"
+.\" The contents of this file are subject to the terms of the
+.\" Common Development and Distribution License (the "License").
+.\" You may not use this file except in compliance with the License.
+.\"
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+.\" or http://www.opensolaris.org/os/licensing.
+.\" See the License for the specific language governing permissions
+.\" and limitations under the License.
+.\"
+.\" When distributing Covered Code, include this CDDL HEADER in each
+.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+.\" If applicable, add the following below this CDDL HEADER, with the
+.\" fields enclosed by brackets "[]" replaced with your own identifying
+.\" information: Portions Copyright [yyyy] [name of copyright owner]
+.\"
+.Dd August 6, 2021
+.Dt MEMORY 3C
+.Os
+.Sh NAME
+.Nm memory ,
+.Nm memccpy ,
+.Nm memchr ,
+.Nm memcmp ,
+.Nm memcpy ,
+.Nm memmem ,
+.Nm memmove ,
+.Nm memrchr ,
+.Nm memset
+.Nd memory operations
+.Sh SYNOPSIS
+.In string.h
+.Ft "void *"
+.Fo memccpy
+.Fa "void *restrict s1"
+.Fa "const void *restrict s2"
+.Fa "int c"
+.Fa "size_t n"
+.Fc
+.Ft "void *"
+.Fo memchr
+.Fa "void *s",
+.Fa "int c"
+.Fa "size_t n"
+.Fc
+.Ft int
+.Fo memcmp
+.Fa "const void *s1"
+.Fa "const void *s2"
+.Fa "size_t n"
+.Fc
+.Ft "void *"
+.Fo memcpy
+.Fa "void *restrict s1"
+.Fa "const void *restrict s2"
+.Fa "size_t n"
+.Fc
+.Ft "void *"
+.Fo memmem
+.Fa "const void *l"
+.Fa "size_t l_len"
+.Fa "const void *s"
+.Fa "size_t s_len"
+.Fc
+.Ft "void *"
+.Fo memmove
+.Fa "void *s1"
+.Fa "const void *s2"
+.Fa "size_t n"
+.Fc
+.Ft "void *"
+.Fo memrchr
+.Fa "void *s",
+.Fa "int c"
+.Fa "size_t n"
+.Fc
+.Ft "void *"
+.Fo memset
+.Fa "void *s"
+.Fa int c
+.Fa "size_t n"
+.Fc
+.Sh DESCRIPTION
These functions operate as efficiently as possible on memory areas (arrays of
-bytes bounded by a count, not terminated by a null character). They do not
-check for the overflow of any receiving memory area.
-.sp
-.LP
-The \fBmemccpy()\fR function copies bytes from memory area \fIs2\fR into
-\fIs1\fR, stopping after the first occurrence of \fIc\fR (converted to an
-\fBunsigned char\fR) has been copied, or after \fIn\fR bytes have been copied,
-whichever comes first. It returns a pointer to the byte after the copy of
-\fIc\fR in \fIs1\fR, or a null pointer if \fIc\fR was not found in the first
-\fIn\fR bytes of \fIs2\fR.
-.sp
-.LP
-The \fBmemchr()\fR function returns a pointer to the first occurrence of
-\fIc\fR (converted to an \fBunsigned char\fR) in the first \fIn\fR bytes (each
-interpreted as an \fBunsigned char\fR) of memory area \fIs\fR, or a null
-pointer if \fIc\fR does not occur.
-.sp
-.LP
-The \fBmemcmp()\fR function compares its arguments, looking at the first
-\fIn\fR bytes (each interpreted as an \fBunsigned char\fR), and returns an
-integer less than, equal to, or greater than 0, according as \fIs1\fR is
-lexicographically less than, equal to, or greater than \fIs2\fR when taken to
-be unsigned characters.
-.sp
-.LP
-The \fBmemcpy()\fR function copies \fIn\fR bytes from memory area \fIs2\fR to
-\fIs1\fR. It returns \fIs1\fR. If copying takes place between objects that
-overlap, the behavior is undefined.
-.sp
-.LP
-The \fBmemmem()\fR function searches for the \fIs_len\fR long byte pattern
-\fIs\fR in the memory region starting at \fIl\fR for \fIl_len\fR bytes. If a
-match is found, a pointer to the starting location in \fIl\fR is returned. If no
-match is found, \fIl_len\fR is zero, \fIs_len\fR is zero, or \fIl_len\fR is less
-than \fIs_len\fR, then a null pointer is return.
-.sp
-.LP
-The \fBmemmove()\fR function copies \fIn\fR bytes from memory area \fIs2\fR to
-memory area \fIs1\fR. Copying between objects that overlap will take place
-correctly. It returns \fIs1\fR.
-.sp
-.LP
-The \fBmemset()\fR function sets the first \fIn\fR bytes in memory area \fIs\fR
-to the value of \fIc\fR (converted to an \fBunsigned char\fR). It returns
-\fIs\fR.
-.SH USAGE
-.LP
-Using \fBmemcpy()\fR might be faster than using \fBmemmove()\fR if the
+bytes bounded by a count, not terminated by a null character).
+They do not check for the overflow of any receiving memory area.
+.Pp
+The
+.Fn memccpy
+function copies bytes from memory area
+.Fa s2
+into
+.Fa s1 ,
+stopping after the first occurrence of
+.Fa c
+.Po
+converted to an
+.Vt unsigned char
+.Pc
+has been copied, or after
+.Fa n
+bytes have been copied, whichever comes first.
+It returns a pointer to the byte after the copy of
+.Fa c
+in
+.Fa s1 ,
+or a
+.Dv NULL
+pointer if
+.Fa c
+was not found in the first
+.Fa n
+bytes of
+.Fa s2 .
+.Pp
+The
+.Fn memchr
+function returns a pointer to the first occurrence of
+.Fa c
+.Po
+converted to an
+.Vt unsigned char
+.Pc
+in the first
+.Fa n
+bytes
+.Po
+each interpreted as an
+.Vt unsigned char
+.Pc
+of memory area
+.Fa s ,
+or a
+.Dv NULL
+pointer if
+.Fa c
+does not occur.
+.Pp
+The
+.Fn memrchr
+function behaves similarly to the
+.Fn memchr
+function, except that the memory area is searched in reverse from the
+last byte.
+.Pp
+The
+.Fn memcmp
+function compares its arguments, looking at the first
+.Fa n
+bytes
+.Po
+each interpreted as an
+.Vt unsigned char
+.Pc ,
+and returns an integer less than, equal to, or greater than 0, according as
+.Fa s1
+is less than, equal to, or greater than
+.Fa s2
+when taken to be unsigned characters.
+.Pp
+The
+.Fn memcpy
+function copies
+.Fa n
+bytes from memory area
+.Fa s2
+to
+.Fa s1
+It returns
+.Fa s1 .
+If copying takes place between objects that overlap, the behavior is undefined.
+In such cases, use
+.Fn memmove
+instead.
+.Pp
+The
+.Fn memmem
+function searches for the
+.Fa s_len
+long byte pattern
+.Fa s
+in the memory region starting at
+.Fa l
+for
+.Fa l_len
+bytes.
+If a match is found, a pointer to the starting location in
+.Fa l
+is returned.
+If no match is found,
+.Fa l_len
+is zero,
+.Fa s_len
+is zero, or
+.Fa l_len
+is less than
+.Fa s_len
+then a
+.Dv NULL
+pointer is return.
+.Pp
+The
+.Fn memmove
+function copies
+.Fa n
+bytes from memory area
+.Fa s2
+to memory area
+.Fa s1 .
+Copying between objects that overlap will take place correctly.
+It returns
+.Fa s1 .
+.Pp
+The
+.Fn memset
+function sets the first
+.Fa n
+bytes in memory area
+.Fa s
+to the value of
+.Fa c
+.Po
+converted to an
+.Vt unsigned char
+.Pc .
+It returns
+.Fa s .
+.Sh USAGE
+Using
+.Fn memcpy
+might be faster than using
+.Fn memmove
+if the
application knows that the objects being copied do not overlap.
-.SH ATTRIBUTES
-.LP
-See \fBattributes\fR(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE ATTRIBUTE VALUE
-_
-Interface Stability Stable
-_
-MT-Level MT-Safe
-_
-Standard See \fBstandards\fR(5).
-.TE
-
-.SH SEE ALSO
-.LP
-\fBstring\fR(3C), \fBattributes\fR(5), \fBstandards\fR(5)
-.SH NOTES
-.LP
-Overlap between objects being copied can arise even when their (virtual)
+.Sh INTERFACE STABILITY
+.Sy Committed
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr string 3C ,
+.Xr attributes 5 ,
+.Xr standards 5
+.Sh NOTES
+Overlap between objects being copied can arise even when their
+.Pq virtual
address ranges appear to be disjoint; for example, as a result of
memory-mapping overlapping portions of the same underlying file, or of
attaching the same shared memory segment more than once.
diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc
index 70c9189c0f..b45024541a 100644
--- a/usr/src/pkg/manifests/system-library.man3c.inc
+++ b/usr/src/pkg/manifests/system-library.man3c.inc
@@ -1029,6 +1029,7 @@ link path=usr/share/man/man3c/memcmp.3c target=memory.3c
link path=usr/share/man/man3c/memcpy.3c target=memory.3c
link path=usr/share/man/man3c/memmem.3c target=memory.3c
link path=usr/share/man/man3c/memmove.3c target=memory.3c
+link path=usr/share/man/man3c/memrchr.3c target=memory.3c
link path=usr/share/man/man3c/memset.3c target=memory.3c
link path=usr/share/man/man3c/minor.3c target=makedev.3c
link path=usr/share/man/man3c/mkdtemp.3c target=mkstemp.3c
diff --git a/usr/src/pkg/manifests/system-test-libctest.mf b/usr/src/pkg/manifests/system-test-libctest.mf
index adb232e2ef..68b534512b 100644
--- a/usr/src/pkg/manifests/system-test-libctest.mf
+++ b/usr/src/pkg/manifests/system-test-libctest.mf
@@ -99,6 +99,8 @@ file path=opt/libc-tests/tests/fpround_test.$(ARCH64) mode=0555
file path=opt/libc-tests/tests/i18n/bindtextdomain_test mode=0555
file path=opt/libc-tests/tests/i18n/bindtextdomain_test.$(ARCH) mode=0555
file path=opt/libc-tests/tests/i18n/bindtextdomain_test.$(ARCH64) mode=0555
+file path=opt/libc-tests/tests/memchr.32 mode=0555
+file path=opt/libc-tests/tests/memchr.64 mode=0555
file path=opt/libc-tests/tests/memset_s.32 mode=0555
file path=opt/libc-tests/tests/memset_s.64 mode=0555
file path=opt/libc-tests/tests/newlocale_test mode=0555
diff --git a/usr/src/test/libc-tests/cfg/symbols/string_h.cfg b/usr/src/test/libc-tests/cfg/symbols/string_h.cfg
index 64506eb9dd..890d5f6459 100644
--- a/usr/src/test/libc-tests/cfg/symbols/string_h.cfg
+++ b/usr/src/test/libc-tests/cfg/symbols/string_h.cfg
@@ -29,5 +29,13 @@ type | locale_t | string.h | -ALL +SUSv4+
#
# Functions
#
+func | memchr |\
+ void * |\
+ const void *; int; size_t |\
+ string.h | ALL
+func | memrchr |\
+ void * |\
+ const void *; int; size_t |\
+ string.h | -ALL
func | strerror_l | char * | int; locale_t | string.h |\
-ALL +SUSv4+
diff --git a/usr/src/test/libc-tests/runfiles/default.run b/usr/src/test/libc-tests/runfiles/default.run
index 72846a3a46..4b6d1a4a74 100644
--- a/usr/src/test/libc-tests/runfiles/default.run
+++ b/usr/src/test/libc-tests/runfiles/default.run
@@ -108,6 +108,8 @@ timeout = 600
[/opt/libc-tests/tests/env-7076.64]
[/opt/libc-tests/tests/fnmatch.32]
[/opt/libc-tests/tests/fnmatch.64]
+[/opt/libc-tests/tests/memchr.32]
+[/opt/libc-tests/tests/memchr.64]
[/opt/libc-tests/tests/memset_s.32]
[/opt/libc-tests/tests/memset_s.64]
[/opt/libc-tests/tests/posix_memalign.32]
diff --git a/usr/src/test/libc-tests/tests/Makefile b/usr/src/test/libc-tests/tests/Makefile
index c15aeba252..d818722d41 100644
--- a/usr/src/test/libc-tests/tests/Makefile
+++ b/usr/src/test/libc-tests/tests/Makefile
@@ -42,6 +42,7 @@ PROGS = \
endian \
env-7076 \
fnmatch \
+ memchr \
memset_s \
posix_memalign \
printf-9511 \
diff --git a/usr/src/test/libc-tests/tests/memchr.c b/usr/src/test/libc-tests/tests/memchr.c
new file mode 100644
index 0000000000..9ec3c6e0ef
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/memchr.c
@@ -0,0 +1,294 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2021 Oxide Computer Company
+ */
+
+/*
+ * Various tests for memchr() and memrchr(). Note, this test assumes that the
+ * system is either ILP32 or LP64 with an 8-bit unsigned char due to the tests
+ * that are explicitly looking at making sure memchr() and memrchr() truncate
+ * correctly.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <err.h>
+#include <stdlib.h>
+
+/*
+ * memchr_buf is a page sized buffer surrounded by two PROT_NONE pages which are
+ * meant to try and catch us walking over the edge of the buffer.
+ */
+static uint8_t *memchr_buf;
+static size_t memchr_buflen;
+
+static void
+memchr_setup(void)
+{
+ size_t pgsz = getpagesize();
+ void *addr;
+
+ if (pgsz <= 0) {
+ err(EXIT_FAILURE, "failed to get system page size");
+ }
+
+ addr = mmap(NULL, 3 * pgsz, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (addr == MAP_FAILED) {
+ err(EXIT_FAILURE, "failed to mmap %zu bytes", 3 * pgsz);
+ }
+
+ memchr_buf = (uint8_t *)addr + pgsz;
+ memchr_buflen = pgsz;
+
+ if (mprotect(addr, pgsz, PROT_NONE) != 0) {
+ err(EXIT_FAILURE, "failed to protect leading PROT_NONE guard "
+ "at %p", addr);
+ }
+
+ addr = (uint8_t *)addr + 2 * pgsz;
+ if (mprotect(addr, pgsz, PROT_NONE) != 0) {
+ err(EXIT_FAILURE, "failed to protect trailing PROT_NONE guard "
+ "at %p", addr);
+ }
+}
+
+static boolean_t
+memchr_basic(void)
+{
+ boolean_t ret = B_TRUE;
+ const void *targ;
+ const void *found;
+
+ (void) memset(memchr_buf, 0, memchr_buflen);
+ memchr_buf[0] = 'r';
+
+ if ((found = memchr(memchr_buf, 'r', memchr_buflen)) != memchr_buf) {
+ warnx("TEST FAILED: memchr failed to find 'r' (1), found %p, "
+ "expected %p", found, memchr_buf);
+ ret = B_FALSE;
+ }
+
+ if ((found = memrchr(memchr_buf, 'r', memchr_buflen)) != memchr_buf) {
+ warnx("TEST FAILED: memrchr failed to find 'r' (1), found %p, "
+ "expected %p", found, memchr_buf);
+ ret = B_FALSE;
+ }
+
+ memchr_buf[memchr_buflen - 1] = 'r';
+ targ = &memchr_buf[memchr_buflen - 1];
+
+ if ((found = memchr(memchr_buf, 'r', memchr_buflen)) != memchr_buf) {
+ warnx("TEST FAILED: memchr failed to find 'r' (2), found %p, "
+ "expected %p", found, memchr_buf);
+ ret = B_FALSE;
+ }
+
+ if ((found = memrchr(memchr_buf, 'r', memchr_buflen)) != targ) {
+ warnx("TEST FAILED: memrchr failed to find 'r' (2), found %p, "
+ "expected %p", found, targ);
+ warnx("TEST FAILED: memchr failed to find 'r'");
+ ret = B_FALSE;
+ }
+
+ memchr_buf[0] = 0;
+
+ if ((found = memchr(memchr_buf, 'r', memchr_buflen)) != targ) {
+ warnx("TEST FAILED: memchr failed to find 'r' (3), found %p, "
+ "expected %p", found, targ);
+ ret = B_FALSE;
+ }
+
+ if ((found = memrchr(memchr_buf, 'r', memchr_buflen)) != targ) {
+ warnx("TEST FAILED: memrchr failed to find 'r' (3), found %p, "
+ "expected %p", found, targ);
+ ret = B_FALSE;
+ }
+
+ if (ret) {
+ (void) printf("TEST PASSED: basic memchr() and memrchr()\n");
+ }
+ return (ret);
+}
+
+static boolean_t
+memchr_notfound(void)
+{
+ boolean_t ret = B_TRUE;
+ const void *found;
+
+ (void) memset(memchr_buf, 0x23, memchr_buflen);
+
+ if ((found = memchr(memchr_buf, 0, memchr_buflen)) != NULL) {
+ warnx("TEST FAILED: memchr unexpectedly found value (1), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ if (memrchr(memchr_buf, 0, memchr_buflen) != NULL) {
+ warnx("TEST FAILED: memrchr unexpectedly found value (1), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ if (memchr(memchr_buf, 0x24, memchr_buflen) != NULL) {
+ warnx("TEST FAILED: memchr unexpectedly found value (2), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ if (memrchr(memchr_buf, 0x24, memchr_buflen) != NULL) {
+ warnx("TEST FAILED: memrchr unexpectedly found value (2), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ memchr_buf[1] = 0x24;
+
+ if (memchr(memchr_buf, 0x24, 1) != NULL) {
+ warnx("TEST FAILED: memchr unexpectedly found value (3), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ if (memrchr(memchr_buf, 0x24, 1) != NULL) {
+ warnx("TEST FAILED: memrchr unexpectedly found value (3), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ memchr_buf[1] = 0x24;
+
+ if (memchr(memchr_buf + 1, 0x23, 1) != NULL) {
+ warnx("TEST FAILED: memchr unexpectedly found value (4), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ if (memrchr(memchr_buf + 1, 0x23, 1) != NULL) {
+ warnx("TEST FAILED: memrchr unexpectedly found value (4), "
+ "found %p, expected %p", found, NULL);
+ ret = B_FALSE;
+ }
+
+ if (ret) {
+ (void) printf("TEST PASSED: memchr() and memrchr() on "
+ "missing values\n");
+ }
+
+ return (ret);
+}
+
+static boolean_t
+memchr_truncation(void)
+{
+ boolean_t ret = B_TRUE;
+ const void *found;
+ const void *targ;
+
+ (void) memset(memchr_buf, 0x42, memchr_buflen);
+
+ if ((found = memchr(memchr_buf, 0x42, memchr_buflen)) != memchr_buf) {
+ warnx("TEST FAILED: memchr failed to find 0x42, found %p, "
+ "expected %p", found, memchr_buf);
+ ret = B_FALSE;
+ }
+
+ targ = &memchr_buf[memchr_buflen - 1];
+
+ if ((found = memrchr(memchr_buf, 0x42, memchr_buflen)) != targ) {
+ warnx("TEST FAILED: memrchr failed to find 0x42, found %p, "
+ "expected %p", found, targ);
+ ret = B_FALSE;
+ }
+
+ if ((found = memchr(memchr_buf, 0x430042, memchr_buflen)) !=
+ memchr_buf) {
+ warnx("TEST FAILED: memchr failed to find 0x42 with 0x430042, "
+ "found %p, expected %p", found, memchr_buf);
+ ret = B_FALSE;
+ }
+
+ if ((found = memrchr(memchr_buf, 0x430042, memchr_buflen)) != targ) {
+ warnx("TEST FAILED: memrchr failed to find 0x42 with 0x430042, "
+ "found %p, expected %p", found, targ);
+ ret = B_FALSE;
+ }
+
+ /*
+ * -190 is -0xbe, which when cast to an unsigned char will be 0x42.
+ */
+ if ((found = memchr(memchr_buf, -190, memchr_buflen)) != memchr_buf) {
+ warnx("TEST FAILED: memchr failed to find 0x42 with -190, "
+ "found %p, expected %p", found, memchr_buf);
+ ret = B_FALSE;
+ }
+
+ if ((found = memrchr(memchr_buf, -190, memchr_buflen)) != targ) {
+ warnx("TEST FAILED: memrchr failed to find 0x42 with -190, "
+ "found %p, expected %p", found, targ);
+ ret = B_FALSE;
+ }
+
+ if ((found = memchr(memchr_buf, -190, memchr_buflen)) != memchr_buf) {
+ warnx("TEST FAILED: memchr failed to find 0x42 with -190, "
+ "found %p, expected %p", found, memchr_buf);
+ ret = B_FALSE;
+ }
+
+ if ((found = memrchr(memchr_buf, -190, memchr_buflen)) != targ) {
+ warnx("TEST FAILED: memrchr failed to find 0x42 with -190, "
+ "found %p, expected %p", found, targ);
+ ret = B_FALSE;
+ }
+
+ if ((found = memchr(memchr_buf, 0x42424200, memchr_buflen)) != NULL) {
+ warnx("TEST FAILED: memchr somehow found 0x42 with "
+ "0x42424200, found %p, expected NULL", found);
+ ret = B_FALSE;
+ }
+
+ if ((found = memrchr(memchr_buf, 0x42424200, memchr_buflen)) != NULL) {
+ warnx("TEST FAILED: memrchr somehow found 0x42 with "
+ "0x42424200, found %p, expected NULL", found);
+ ret = B_FALSE;
+ }
+
+ if (ret) {
+ (void) printf("TEST PASSED: truncated values\n");
+ }
+
+ return (B_TRUE);
+}
+
+int
+main(void)
+{
+ int ret = EXIT_SUCCESS;
+
+ memchr_setup();
+
+ if (!memchr_basic())
+ ret = EXIT_FAILURE;
+ if (!memchr_notfound())
+ ret = EXIT_FAILURE;
+ if (!memchr_truncation())
+ ret = EXIT_FAILURE;
+
+ if (ret == EXIT_SUCCESS) {
+ (void) printf("All tests passed successfully\n");
+ }
+
+ return (ret);
+}