untrusted comment: verify with openbsd-78-base.pub
RWS3/nvFmk4SWYvKNJoRyTxFgb0nv2EMv2746MP2ry0y+vnkCUqUkOP2otuiTZQzBPdk0MrvCe3bKzyqMQqkC//e+taZKSAmuA0=

OpenBSD 7.8 errata 018, March 4, 2026:

Make the pledge(2) mechanism which permits specific libc paths more
strict by removing the "tmppath" promise, avoid normalizing paths
which libc already creates strictly correct, and blocking '..'
traversals out of /usr/share/zoneinfo.

Apply by doing:
    signify -Vep /etc/signify/openbsd-78-base.pub -x 018_pledgepaths.patch.sig \
        -m - | (cd /usr/src && patch -p0)

And then rebuild and install a new kernel:
    KK=`sysctl -n kern.osversion | cut -d# -f1`
    cd /usr/src/sys/arch/`machine`/compile/$KK
    make obj
    make config
    make
    make install
    cp /usr/src/lib/libc/sys/pledge.2 /usr/share/man/man2/pledge.2

Index: sys/kern/kern_pledge.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_pledge.c,v
diff -u -p -u -r1.333.2.1 kern_pledge.c
--- sys/kern/kern_pledge.c	27 Feb 2026 20:38:14 -0000	1.333.2.1
+++ sys/kern/kern_pledge.c	3 Mar 2026 05:05:02 -0000
@@ -82,7 +82,6 @@
 uint64_t pledgereq_flags(const char *req);
 int	 parsepledges(struct proc *p, const char *kname,
 	    const char *promises, u_int64_t *fp);
-int	 canonpath(const char *input, char *buf, size_t bufsize);
 void	 unveil_destroy(struct process *ps);
 
 /*
@@ -296,7 +295,7 @@ const uint64_t pledge_syscalls[SYS_MAXSY
 	[SYS_fstatat] = PLEDGE_RPATH | PLEDGE_WPATH,
 	[SYS_faccessat] = PLEDGE_RPATH | PLEDGE_WPATH,
 	[SYS_readlinkat] = PLEDGE_RPATH | PLEDGE_WPATH,
-	[SYS_lstat] = PLEDGE_RPATH | PLEDGE_WPATH | PLEDGE_TMPPATH,
+	[SYS_lstat] = PLEDGE_RPATH | PLEDGE_WPATH,
 	[SYS_truncate] = PLEDGE_WPATH,
 	[SYS_rename] = PLEDGE_RPATH | PLEDGE_CPATH,
 	[SYS_rmdir] = PLEDGE_CPATH,
@@ -305,7 +304,7 @@ const uint64_t pledge_syscalls[SYS_MAXSY
 	[SYS_linkat] = PLEDGE_CPATH,
 	[SYS_symlink] = PLEDGE_CPATH,
 	[SYS_symlinkat] = PLEDGE_CPATH,
-	[SYS_unlink] = PLEDGE_CPATH | PLEDGE_TMPPATH,
+	[SYS_unlink] = PLEDGE_CPATH,
 	[SYS_unlinkat] = PLEDGE_CPATH,
 	[SYS_mkdir] = PLEDGE_CPATH,
 	[SYS_mkdirat] = PLEDGE_CPATH,
@@ -395,7 +394,6 @@ static const struct {
 	{ "settime",		PLEDGE_SETTIME },
 	{ "stdio",		PLEDGE_STDIO },
 	{ "tape",		PLEDGE_TAPE },
-	{ "tmppath",		PLEDGE_TMPPATH },
 	{ "tty",		PLEDGE_TTY },
 	{ "unix",		PLEDGE_UNIX },
 	{ "unveil",		PLEDGE_UNVEIL },
@@ -500,7 +498,7 @@ sys_pledge(struct proc *p, void *v, regi
 		atomic_setbits_int(&pr->ps_flags, PS_PLEDGE);
 
 		if ((pr->ps_pledge & (PLEDGE_RPATH | PLEDGE_WPATH |
-		    PLEDGE_CPATH | PLEDGE_DPATH | PLEDGE_TMPPATH | PLEDGE_EXEC |
+		    PLEDGE_CPATH | PLEDGE_DPATH | PLEDGE_EXEC |
 		    PLEDGE_UNIX | PLEDGE_UNVEIL)) == 0)
 			unveil_cleanup = 1;
 	}
@@ -589,11 +587,9 @@ pledge_fail(struct proc *p, int error, u
  * without the right flags set
  */
 int
-pledge_namei(struct proc *p, struct nameidata *ni, char *origpath)
+pledge_namei(struct proc *p, struct nameidata *ni, char *path)
 {
-	char path[PATH_MAX];
 	uint64_t pledge;
-	int error;
 
 	if ((p->p_p->ps_flags & PS_PLEDGE) == 0 ||
 	    (p->p_p->ps_flags & PS_COREDUMP))
@@ -612,29 +608,6 @@ pledge_namei(struct proc *p, struct name
 	if ((ni->ni_pledge & PLEDGE_EXEC) && (pledge & PLEDGE_EXEC))
 		return (0);
 
-	error = canonpath(origpath, path, sizeof(path));
-	if (error)
-		return (error);
-
-	/* Detect what looks like a mkstemp(3) family operation */
-	if ((pledge & PLEDGE_TMPPATH) &&
-	    (p->p_pledge_syscall == SYS_open) &&
-	    (ni->ni_pledge & PLEDGE_CPATH) &&
-	    strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
-		ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
-		return (0);
-	}
-
-	/* Allow unlinking of a mkstemp(3) file...
-	 * Good opportunity for strict checks here.
-	 */
-	if ((pledge & PLEDGE_TMPPATH) &&
-	    (p->p_pledge_syscall == SYS_unlink) &&
-	    strncmp(path, "/tmp/", sizeof("/tmp/") - 1) == 0) {
-		ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
-		return (0);
-	}
-
 	/* Whitelisted paths */
 	switch (p->p_pledge_syscall) {
 	case SYS_access:
@@ -705,6 +678,13 @@ pledge_namei(struct proc *p, struct name
 		if ((ni->ni_pledge == PLEDGE_RPATH) &&
 		    strncmp(path, "/usr/share/zoneinfo/",
 		    sizeof("/usr/share/zoneinfo/") - 1) == 0)  {
+			const char *cp;
+
+			for (cp = path + sizeof("/usr/share/zoneinfo/") - 2;
+			    *cp; cp++)
+				if (cp[0] == '/' && cp[1] == '.' && cp[2] == '.' &&
+				    (cp[3] == '/' || cp[3] == '\0'))
+					goto nozoneinfo;
 			ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
 			return (0);
 		}
@@ -713,7 +693,7 @@ pledge_namei(struct proc *p, struct name
 			ni->ni_cnd.cn_flags |= BYPASSUNVEIL;
 			return (0);
 		}
-
+nozoneinfo:
 		break;
 	case SYS_stat:
 		/* XXX go library stats /etc/hosts, remove this soon */
@@ -1609,45 +1589,4 @@ pledge_protexec(struct proc *p, int prot
 	if (!(p->p_pledge & PLEDGE_PROTEXEC) && (prot & PROT_EXEC))
 		return pledge_fail(p, EPERM, PLEDGE_PROTEXEC);
 	return 0;
-}
-
-int
-canonpath(const char *input, char *buf, size_t bufsize)
-{
-	const char *p;
-	char *q;
-
-	/* can't canon relative paths, don't bother */
-	if (input[0] != '/') {
-		if (strlcpy(buf, input, bufsize) >= bufsize)
-			return ENAMETOOLONG;
-		return 0;
-	}
-
-	p = input;
-	q = buf;
-	while (*p && (q - buf < bufsize)) {
-		if (p[0] == '/' && (p[1] == '/' || p[1] == '\0')) {
-			p += 1;
-
-		} else if (p[0] == '/' && p[1] == '.' &&
-		    (p[2] == '/' || p[2] == '\0')) {
-			p += 2;
-
-		} else if (p[0] == '/' && p[1] == '.' && p[2] == '.' &&
-		    (p[3] == '/' || p[3] == '\0')) {
-			p += 3;
-			if (q != buf)	/* "/../" at start of buf */
-				while (*--q != '/')
-					continue;
-
-		} else {
-			*q++ = *p++;
-		}
-	}
-	if ((*p == '\0') && (q - buf < bufsize)) {
-		*q = 0;
-		return 0;
-	} else
-		return ENAMETOOLONG;
 }
Index: lib/libc/sys/pledge.2
===================================================================
RCS file: /cvs/src/lib/libc/sys/pledge.2,v
diff -u -p -u -r1.74 pledge.2
--- lib/libc/sys/pledge.2	2 Jul 2025 15:56:32 -0000	1.74
+++ lib/libc/sys/pledge.2	1 Mar 2026 18:35:56 -0000
@@ -254,9 +254,25 @@ but special files can be created using:
 .Xr mkfifo 2 ,
 .Xr mknod 2
 .It Cm tmppath
-A number of system calls are allowed to do operations in the
-.Pa /tmp
-directory, including create, read, or write.
+Deprecated.
+This pledge was designed to satisfy the
+.Xr mkstemp 3
+family of functions.
+The limited filesystem access it provided is now disabled, so the
+promise has been removed and will
+return
+.Er EINVAL .
+It should be replaced by either
+allowing use of the whole filesystem, meaning
+.Qq rpath wpath cpath ,
+or use of
+.Xr unveil 2
+with
+.Fa path
+.Qq /tmp
+and
+.Fa permissions
+.Qq rwc .
 .It Cm inet
 The following system calls are allowed to operate in the
 .Dv AF_INET
