head -r320482 (e.g.): DNINIT_ALL and SOCK_MAXADDRLEN vs. SOCK_MAXADDRLEN+1 use in char buf[<?>] declarations: is the usage all correct?
Mark Millard
markmi at dsl-only.net
Sat Jul 1 00:06:06 UTC 2017
While trying to track down a repeatable crash
for powerpc production kernel's (but not
debug ones) I ran into the following. (I'm
not familiar with the material involved.)
NDINIT_ALL in /usr/src/sys/kern/vfs_lookup.c
records its char* namep argument in ndp->ni_dirp :
/usr/src/sys/kern/uipc_usrreq.c:
char buf[SOCK_MAXADDRLEN];
. . .
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF,
UIO_SYSSPACE, buf, fd, cap_rights_init(&rights, CAP_CONNECTAT), td);
. . .
(elsewhere:)
void
NDINIT_ALL(struct nameidata *ndp, u_long op, u_long flags, enum uio_seg segflg,
const char *namep, int dirfd, struct vnode *startdir, cap_rights_t *rightsp,
struct thread *td)
{
ndp->ni_cnd.cn_nameiop = op;
ndp->ni_cnd.cn_flags = flags;
ndp->ni_segflg = segflg;
ndp->ni_dirp = namep;
ndp->ni_dirfd = dirfd;
ndp->ni_startdir = startdir;
if (rightsp != NULL)
ndp->ni_rightsneeded = *rightsp;
else
cap_rights_init(&ndp->ni_rightsneeded);
filecaps_init(&ndp->ni_filecaps);
ndp->ni_cnd.cn_thread = td;
}
But the size of the buffer supplied in other calls varies
from the size used for this call:
/usr/src/sys/fs/nfsclient/nfs_clvfsops.c: if (args.addrlen > SOCK_MAXADDRLEN) {
/usr/src/sys/kern/uipc_syscalls.c: if (len > SOCK_MAXADDRLEN)
/usr/src/sys/kern/uipc_usrreq.c: char buf[SOCK_MAXADDRLEN];
/usr/src/sys/netgraph/ng_ksocket.c: if (pathlen > SOCK_MAXADDRLEN) {
/usr/src/sys/netgraph/ng_ksocket.c: char pathbuf[SOCK_MAXADDRLEN + 1];
/usr/src/sys/sys/socket.h:#define SOCK_MAXADDRLEN 255 /* longest possible addresses */
Some of the other usage of SOCK_MAXADDRLEN is:
if (vfs_getopt(mp->mnt_optnew, "addr",
(void **)&args.addr, &args.addrlen) == 0) {
if (args.addrlen > SOCK_MAXADDRLEN) {
error = ENAMETOOLONG;
goto out;
}
nam = malloc(args.addrlen, M_SONAME, M_WAITOK);
bcopy(args.addr, nam, args.addrlen);
nam->sa_len = args.addrlen;
. . .
if (len > SOCK_MAXADDRLEN)
return (ENAMETOOLONG);
if (len < offsetof(struct sockaddr, sa_data[0]))
return (EINVAL);
sa = malloc(len, M_SONAME, M_WAITOK);
. . .
if ((path = ng_get_string_token(s, off, &toklen, NULL)) == NULL)
return (EINVAL);
pathlen = strlen(path);
if (pathlen > SOCK_MAXADDRLEN) {
free(path, M_NETGRAPH_KSOCKET);
return (E2BIG);
}
if (*buflen < pathoff + pathlen) {
free(path, M_NETGRAPH_KSOCKET);
return (ERANGE);
}
*off += toklen;
bcopy(path, sun->sun_path, pathlen);
That last suggests that pathlen does not include a '\0'
termination character position but that path is supposed
to be '\0' terminated (in order to justify strlen(.) use)
yet the later bcopy avoids the '\0' position for
sun->sun_path.
This makes it unclear which of the following is
more appropriate vs. if each is correct for its
specific usage:
/usr/src/sys/kern/uipc_usrreq.c:
. . .
char buf[SOCK_MAXADDRLEN];
. . .
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF,
UIO_SYSSPACE, buf, fd, cap_rights_init(&rights, CAP_CONNECTAT), td);
(The above buf has an odd number of bytes.)
(The above code is in the execution sequence that leads
to the powerpc fatal trap.)
/usr/src/sys/netgraph/ng_ksocket.c:
. . .
case PF_LOCAL:
{
const int pathoff = OFFSETOF(struct sockaddr_un, sun_path);
const struct sockaddr_un *sun = (const struct sockaddr_un *)sa;
const int pathlen = sun->sun_len - pathoff;
char pathbuf[SOCK_MAXADDRLEN + 1];
char *pathtoken;
bcopy(sun->sun_path, pathbuf, pathlen);
if ((pathtoken = ng_encode_string(pathbuf, pathlen)) == NULL)
return (ENOMEM);
slen += snprintf(cbuf, cbuflen, "local/%s", pathtoken);
free(pathtoken, M_NETGRAPH_KSOCKET);
if (slen >= cbuflen)
return (ERANGE);
*off += sun->sun_len;
return (0);
}
===
Mark Millard
markmi at dsl-only.net
More information about the freebsd-hackers
mailing list