/
githubmirror
/
lsof
Обзор
Документация
Войти
/
githubmirror
/
lsof
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
lib/dialects/hpux/pstat/dfile.c
753 строки
21 KB
Jiajie Chen
Fix potential double free for Lf->fsdir and mp->dir
31 мар 2026, 17:13
31 мар 2026, 17:13
53539a9
Код
Авторство
О чём код?
/* * dfile.c -- pstat-based HP-UX file functions for lsof */ /* * Copyright 1999 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Victor A. Abell * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #ifndef lint static char copyright[] = "@(#) Copyright 1999 Purdue Research Foundation.\nAll rights reserved.\n"; #endif #include "common.h" #if defined(HASNCACHE) /* * Local definitions */ # define DNLCINCR 2048 /* DNLC read increment */ # define NFSIDH \ 256 /* file system ID hash count \ * -- MUST BE A POWER OF TWO!!! */ # define NFSID sizeof(struct psfsid) /* size of psfsid structure */ # define NL_NC sizeof(struct l_nc) /* size of l_nc structure */ # define NPSTM sizeof(struct pst_mpathnode) /* size of pst_mpathnode */ /* * Local structure definitions */ struct l_nc { /* local name cache */ struct psfileid id; /* node's PSTAT ID */ struct psfileid par; /* parent's PSTAT ID */ struct l_nc *pl; /* local parent name cache entry -- * NULL if not found or not yet * accessed (see pls) */ int pls; /* status of pl: 0 = not accessed * 1 = accessed */ int nl; /* name length */ char *nm; /* name */ struct l_nc *next; /* next hash bucket link */ }; struct l_fic { /* file system ID cache */ struct psfsid fsid; /* file system ID */ int nc; /* files cached for file system */ struct l_fic *next; /* next hash bucket link */ }; /* * Local static variables */ static int Nceh; /* number of Nchash entries allocated */ static struct l_nc **Nchash = (struct l_nc **)NULL; /* the name cache hash buckets */ static int Ncmask; /* power of two mask for the name * cache -- sized from Nc */ static int Ndnlc; /* number of DNLC entries via * pst_dynamic.psd_dnlc_size */ static struct l_fic **Ncfsid = (struct l_fic **)NULL; /* the file system hash buckets */ static struct pst_fid Nzpf; /* zeroed pst_fid (for memcmp()) */ static struct psfileid Nzps; /* zeroed psfilid (for memcmp()) */ static int Nzpfs = 0; /* Nzpf status: 1 = zeroed */ static int Nzpss = 0; /* Nzps status: 1 = zeroed */ /* * Local macros */ # define HASHFSID(i) \ (Ncfsid + \ (((int)(((((struct psfsid *)i)->psfs_id * 31415) << 3) & 0xfffffff) + \ (int)((((((struct psfsid *)i)->psfs_type * 31415) << 5) & \ 0xfffffff))) & \ (NFSIDH - 1))) # define HASHPSFILEID(p) \ (Nchash + \ (((int)(((int)((((struct psfileid *)p)->psf_fsid.psfs_id * 31415) \ << 3) & \ 0xfffffff) + \ (int)(((((struct psfileid *)p)->psf_fsid.psfs_type * 31415) \ << 5) & \ 0xfffffff) + \ (int)(((((struct psfileid *)p)->psf_fileid * 31415) << 7) & \ 0xfffffff))) & \ Ncmask)) /* * Local function prototypes */ static struct l_nc *ncache_addr(struct psfileid *ps); static void ncache_free(void); static int ncache_isroot(struct psfileid *ps); static void ncache_size(void); #endif /* defined(HASNCACHE) */ #if defined(HASIPv6) /* * gethostbyname2() -- an RFC2133-compatible get-host-by-name-two function * to get AF_INET and AF_INET6 addresses from host names, * using the gethostbyname() and RFC2553-compatible * getipnodebyname() functions */ extern struct hostent * gethostbyname2(char *nm, /* host name */ int prot) /* protocol -- AF_INET or AF_INET6 */ { int err; if (prot == AF_INET) { /* * This shouldn't be necessary if /etc/nsswitch.conf is correct, but * it's a good fail-safe in case /etc/nsswitch.conf is missing or * incorrect. */ return (gethostbyname(nm)); } return (getipnodebyname(nm, prot, 0, &err)); } #endif /* defined(HASIPv6) */ /* * get_max_fd() -- get maximum file descriptor plus one */ int get_max_fd() { struct rlimit r; if (getrlimit(RLIMIT_NOFILE, &r)) return (-1); return (r.rlim_cur); } #if defined(HASNCACHE) /* * ncache_addr() -- get ncache entry address */ static struct l_nc *ncache_addr(struct psfileid *ps) /* parent's psfileid */ { struct l_nc **hp, *lc; for (hp = HASHPSFILEID(ps), lc = *hp; lc; lc = lc->next) { if (!memcmp((void *)ps, (void *)&lc->id, sizeof(struct psfileid))) return (lc); } return ((struct l_nc *)NULL); } /* * ncache_alloc() -- allocate name cache space */ static void ncache_alloc() { if (Nchash || Ncfsid) ncache_free(); (void)ncache_size(); if (!(Nchash = (struct l_nc **)calloc(Nceh, sizeof(struct l_nc *)))) { (void)fprintf(stderr, "%s: can't allocate %d local name cache entries\n", Pn, Nceh); Error(ctx); } if (Ncfsid) return; if (!(Ncfsid = (struct l_fic **)calloc(NFSIDH, sizeof(struct l_fic *)))) { (void)fprintf(stderr, "%s: can't allocate %d local file system cache entries\n", Pn, NFSIDH); Error(ctx); } } /* * ncache_free() -- free previous ncache allocations */ static void ncache_free() { int i; struct l_fic **fh, *fp, *fx; struct l_nc **nh, *np, *nx; if (Ncfsid) { /* * Free file system ID hash bucket contents. */ for (fh = Ncfsid, i = 0; i < NFSIDH; fh++, i++) { for (fp = *fh; fp; fp = fx) { fx = fp->next; (void)free((MALLOC_P *)fp); } Ncfsid[i] = (struct l_fic *)NULL; } } if (Nchash) { /* * Free name cache. */ for (i = 0, nh = Nchash; i < Nceh; i++, nh++) { for (np = *nh; np; np = nx) { nx = np->next; if (np->nm) (void)free((MALLOC_P *)np->nm); (void)free((MALLOC_P *)np); } } (void)free((MALLOC_P *)Nchash); Nchash = (struct l_nc **)NULL; } } /* * ncache_isroot() -- does psfileid represent the root of a file system? */ static int ncache_isroot(struct psfileid *ps) /* psfileid */ { if (!ps->psf_fsid.psfs_id && !ps->psf_fsid.psfs_type && ps->psf_fileid == -1) return (1); # if defined(HASFSINO) if (!Lf->fs_ino || (Lf->inp_ty != 1) || !Lf->dev_def) return (0); if ((Lf->dev == (dev_t)ps->psf_fsid.psfs_id) && (Lf->fs_ino == (unsigned long)ps->psf_fileid)) return (1); # endif /* defined(HASFSINO) */ return (0); } /* * ncache_load() -- load name cache */ void ncache_load() { if (!Fncache) return; (void)ncache_alloc(); if (!Nzpfs) { (void)memset((void *)&Nzpf, 0, sizeof(Nzpf)); Nzpfs = 1; } if (!Nzpss) { (void)memset((void *)&Nzps, 0, sizeof(Nzps)); Nzpss = 1; } } /* * ncache_loadfs() -- load the name cache for a file system */ struct l_fic *ncache_loadfs(struct psfsid *fsid, /* ID of file system to add */ struct l_fic **fh) /* Ncfsid hash bucket */ { char *cp; struct l_fic *f; int i, nl, nr; struct pst_mpathnode mp[DNLCINCR]; struct l_nc **nh, *nn, *nt, *ntp; int x = 0; /* * Allocate a new file system pointer structure and link it to its bucket. */ if (!(f = (struct l_fic *)malloc(sizeof(struct l_fic)))) { (void)fprintf(stderr, "%s: no fsid structure space\n", Pn); Error(ctx); } f->fsid = *fsid; f->nc = 0; f->next = *fh; *fh = f; while ((nr = pstat_getmpathname(&mp[0], NPSTM, DNLCINCR, x, fsid)) > 0) { x = mp[nr - 1].psr_idx + 1; for (i = 0; i < nr; i++) { /* * Ignore NUL names, ".", and "..". */ if (!(nl = (int)strlen(mp[i].psr_name))) continue; if ((nl < 3) && (mp[i].psr_name[0] == '.')) { if ((nl == 1) || (mp[i].psr_name[1] == '.')) continue; } /* * Allocate name and name cache structure space. */ if (!(cp = (char *)malloc((MALLOC_S)(nl + 1)))) { (void)fprintf(stderr, "%s: no name entry space (%d) for:%s\n", Pn, nl + 1, mp[i].psr_name); Error(ctx); } if (!(nn = (struct l_nc *)malloc(sizeof(struct l_nc)))) { (void)fprintf(stderr, "%s: no name cache entry space (%d) for: %s\n", Pn, (int)sizeof(struct l_nc), mp[i].psr_name); Error(ctx); } /* * Fill in name cache entry, complete with name and name length. */ (void)snpf(cp, nl + 1, "%s", mp[i].psr_name); nn->id = mp[i].psr_file; nn->par = mp[i].psr_parent; nn->nm = cp; nn->nl = nl; nn->pl = nn->next = (struct l_nc *)NULL; nn->pls = 0; nh = HASHPSFILEID(&mp[i].psr_file); /* * Skip to the end of the hash bucket chain, looking for * duplicates along the way. */ for (nt = *nh, ntp = (struct l_nc *)NULL; nt; ntp = nt, nt = nt->next) { if (memcmp((void *)&nt->id, (void *)&nn->id, NL_NC) == 0) break; } if (nt) { /* * Remove a duplicate. */ if (ntp) ntp = nt->next; else *nh = nt->next; (void)free((MALLOC_P *)nt->nm); (void)free((MALLOC_P *)nt); (void)free((MALLOC_P *)nn->nm); (void)free((MALLOC_P *)nn); } else { /* * Link a new entry. */ if (ntp) ntp->next = nn; else *nh = nn; f->nc++; } } if (nr < DNLCINCR) break; } return (f); } /* * ncache_lookup() -- look up a node's name in the kernel's name cache */ char *ncache_lookup(char *buf, /* receiving name buffer */ int blen, /* receiving buffer length */ int *fp) /* full path reply */ { char *cp = buf; int ef; struct l_fic **fh, *fs; struct l_nc *lc; int nl, rlen; char *pc; *cp = '\0'; *fp = 0; # if defined(HASFSINO) /* * If the entry has an inode number that matches the inode number of the * file system mount point, return an empty path reply. That tells the * caller that the already-printed system mount point name is sufficient. */ if (Lf->inp_ty == 1 && Lf->fs_ino && Lf->inode == Lf->fs_ino) return (cp); # endif /* defined(HASFSINO) */ /* * See if cache has been loaded for this pfsid. Don't try to load if cache * loading has been inhibited with -C, or unless the real or effective UID * of this process is root. */ if ((!Myuid || Setuidroot) && Fncache) { for (fh = HASHFSID(&Lf->psfid.psf_fsid), fs = *fh; fs; fs = fs->next) { if (memcmp((void *)&fs->fsid, (void *)&Lf->psfid.psf_fsid, NFSID) == 0) break; } if (!fs) fs = ncache_loadfs(&Lf->psfid.psf_fsid, fh); } else fs = (struct l_fic *)NULL; /* * Search the cache for an entry whose psfileid matches. */ if (!fs || !fs->nc || !(lc = ncache_addr(&Lf->psfid))) { /* * If the node has no cache entry, see if it's the root of the file * system. */ # if defined(HASFSINO) if (Lf->fs_ino && (Lf->inp_ty == 1) && (Lf->fs_ino == Lf->inode)) return (cp); # endif /* defined(HASFSINO) */ /* * If the file system's cache couldn't be loaded -- e.g., this lsof * process lacks permission to load it or cache lookup is inhibited * with -C -- but the UID of the file's process matches the UID of the * lsof process, see if it's possible to read the single path name for * this particular file. (The file must have a non-zero opaque ID.) */ if (!fs) { if (Fncache && (Myuid == Lp->uid) && memcmp((void *)&Lf->opfid, (void *)&Nzpf, sizeof(Nzpf)) && (nl = pstat_getpathname(buf, (blen - 1), &Lf->opfid)) > 0) { buf[nl] = '\0'; if (*buf == '/') *fp = 1; return (buf); } } return ((char *)NULL); } if (ncache_isroot(&lc->id)) { /* * If the node is the root of the file system, return a response * that will cause the root directory to be displayed. */ return (cp); } /* * Start the path assembly. */ if ((nl = lc->nl) > (blen - 1)) return ((char *)NULL); cp = buf + blen - nl - 1; rlen = blen - nl - 1; (void)snpf(cp, nl + 1, "%s", lc->nm); /* * Look up the name cache entries that are parents of the node address. * Quit when: * * there's no parent; * the file system root is reached; * the name length is too large to fit in the receiving buffer. */ for (ef = 0; !ef;) { if (!lc->pl) { if (!lc->pls) { /* * If there is a parent, look up its Ncache address; * otherwise quit on an incomplete path assembly. */ if (memcmp((void *)&lc->par, (void *)&Nzps, sizeof(Nzps))) { lc->pl = ncache_addr(&lc->par); lc->pls = 1; } else break; } } if (ncache_isroot(&lc->par)) { /* * If the parent entry is the file system root, enter the file * system root directory, and indicate that the assembly should * stop after this entry. */ if (!(pc = Lf->fsdir)) break; nl = (int)strlen(pc); ef = 1; } else { /* * Use the parent link if it exists; otherwise exit on an * incomplete path assembly. */ if (!(lc = lc->pl)) break; pc = lc->nm; nl = lc->nl; } /* * Prefix the next path component. Intersperse a '/' if the * component doesn't end in one. */ if (!nl) break; if (pc[nl - 1] != '/') { if (1 > rlen) break; *(cp - 1) = '/'; cp--; rlen--; } if (nl > rlen) break; (void)strncpy((cp - nl), pc, nl); cp -= nl; rlen -= nl; if (ef) { /* * If the file system root directory was just prefixed, return * a full-path indication. */ *fp = 1; break; } } return (cp); } /* * ncache_size() -- get DNLC size */ static void ncache_size() { struct pst_dynamic pd; if (pstat_getdynamic(&pd, sizeof(pd), 1, 0) != 1) { (void)fprintf(stderr, "%s: can't get dynamic status\n", Pn); Error(ctx); } Ndnlc = (int)pd.psd_dnlc_size; for (Nceh = 1; Nceh < (Ndnlc + Ndnlc); Nceh <<= 1) ; Ncmask = Nceh - 1; } #endif /* defined(HASNCACHE) */ /* * print_dev() -- print device */ char *print_dev(struct lfile *lf, /* file whose device is to be printed */ dev_t *dev) /* device to be printed */ { static char buf[128]; (void)snpf(buf, sizeof(buf), "%d,%#x", GET_MAJ_DEV(*dev), GET_MIN_DEV(*dev)); return (buf); } /* * process_finfo() -- process file information */ void process_finfo(pd, opfid, psfid, na) struct pst_filedetails *pd; /* file details */ struct pst_fid *opfid; /* opaque file ID for this file */ struct psfileid *psfid; /* PSTAT file ID for this file */ KA_T na; /* node address */ { char *cp, buf[32]; enum lsof_file_type type; uint32_t unknown_file_type_number = 0; dev_t dev; int devs = 0; int32_t lk; struct mounts *mp; /* * Save file IDs for later use in name lookup. */ Lf->opfid = *opfid; Lf->psfid = *psfid; #if defined(HASFSTRUCT) /* * Save node ID. */ if (na) { Lf->fna = na; Lf->fsv |= FSV_NI; } #endif /* defined(HASFSTRUCT) */ /* * Construct lock code. */ if ((lk = pd->psfd_lckflag) & PS_FPARTRDLCK) Lf->lock = LSOF_LOCK_READ_PARTIAL; else if (lk & PS_FPARTWRLCK) Lf->lock = LSOF_LOCK_WRITE_PARTIAL; else if (lk & PS_FFULLRDLCK) Lf->lock = LSOF_LOCK_READ_FULL; else if (lk & PS_FFULLWRLCK) Lf->lock = LSOF_LOCK_WRITE_FULL; else Lf->lock = LSOF_LOCK_NONE; /* * Derive type from modes. */ switch ((int)(pd->psfd_mode & PS_IFMT)) { case PS_IFREG: type = LSOF_FILE_REGULAR; Ntype = N_REGLR; break; case PS_IFBLK: type = LSOF_FILE_BLOCK; Ntype = N_BLK; break; case PS_IFDIR: type = LSOF_FILE_DIR; Ntype = N_REGLR; break; case PS_IFCHR: type = LSOF_FILE_CHAR; Ntype = N_CHR; break; case PS_IFIFO: type = LSOF_FILE_FIFO; Ntype = N_FIFO; break; default: type = LSOF_FILE_UNKNOWN_RAW; unknown_file_type_number = (pd->psfd_mode & PS_IFMT) >> 12; Ntype = N_REGLR; } if (Lf->type == LSOF_FILE_NONE) { Lf->type = type; Lf->unknown_file_type_number = unknown_file_type_number; } Lf->ntype = Ntype; /* * Save device number. */ switch (Ntype) { case N_FIFO: (void)enter_dev_ch(print_kptr(na, (char *)NULL, 0)); break; default: dev = Lf->dev = (dev_t)pd->psfd_dev; devs = Lf->dev_def = 1; if ((Ntype == N_CHR) || (Ntype == N_BLK)) { Lf->rdev = (dev_t)pd->psfd_rdev; Lf->rdev_def = 1; } } /* * Save node number. */ Lf->inode = (INODETYPE)pd->psfd_ino; Lf->inp_ty = 1; /* * Save link count. */ /* * Ignore a zero link count only if the file is a FIFO. */ if ((Lf->nlink = (long)pd->psfd_nlink) || (Ntype != N_FIFO)) Lf->nlink_def = 1; if (Lf->nlink_def && Nlink && (Lf->nlink < Nlink)) Lf->sf |= SELNLINK; /* * Save file system identity. */ if (devs) { for (mp = readmnt(); mp; mp = mp->next) { if (dev == mp->dev) { Lf->fsdir = mkstrcpy(mp->dir, (MALLOC_S *)NULL); Lf->fsdev = mkstrcpy(mp->fsname, (MALLOC_S *)NULL); #if defined(HASFSINO) Lf->fs_ino = (unsigned long)mp->inode; #endif /* defined(HASFSINO) */ break; } } } else mp = (struct mounts *)NULL; /* * If no offset has been activated and no size saved, activate the offset or * save the size. */ if (!Lf->sz_def) { switch (Ntype) { case N_CHR: case N_FIFO: break; default: Lf->sz = (SZOFFTYPE)pd->psfd_size; Lf->sz_def = 1; } } /* * See if this is an NFS file. */ if (Fnfs) { if (HasNFS < 0) (void)scanmnttab(); if (HasNFS && mp && mp->is_nfs) Lf->sf |= SELNFS; } /* * Test for specified file. */ if (Sfile && is_file_named(NULL, ((Ntype == N_CHR) || (Ntype == N_BLK) ? 1 : 0))) Lf->sf |= SELNM; /* * Enter name characters. */ if (!Lf->nm && Namech[0]) enter_nm(Namech); }