Skip to content

Commit f6d53b3

Browse files
grwilsonallanjude
authored andcommitted
file reference counts can get corrupted
Callers of zfs_file_get and zfs_file_put can corrupt the reference counts for the file structure resulting in a panic or a soft lockup. When zfs send/recv runs, it will add a reference count to the open file, and begin to send or recv the stream. If the file descriptor is closed, then when dmu_recv_stream() or dmu_send() return we will call zfs_file_put to remove the reference we placed on the file structure. Unfortunately, because zfs_file_put() uses the file descriptor to lookup the file structure, it may end up finding that the file descriptor table no longer contains the file struct, thus leaking the file structure. Or it might end up finding a file descriptor for a different file and blindly updating its reference counts. Other failure modes probably exists. This change reworks the zfs_file_[get|put] interface to not rely on the file descriptor but instead pass the zfs_file_t pointer around. Reviewed-by: Matthew Ahrens <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Mark Maybee <[email protected]> Reviewed-by: Ryan Moeller <[email protected]> Co-authored-by: Allan Jude <[email protected]> Signed-off-by: George Wilson <[email protected]> External-issue: DLPX-76119 Closes #12299
1 parent 4965800 commit f6d53b3

File tree

10 files changed

+91
-107
lines changed

10 files changed

+91
-107
lines changed

include/sys/fm/util.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ extern "C" {
3131
#endif
3232

3333
#include <sys/nvpair.h>
34+
#include <sys/zfs_file.h>
3435

3536
/*
3637
* Shared user/kernel definitions for class length, error channel name,
@@ -96,8 +97,8 @@ extern void fm_nvprint(nvlist_t *);
9697
extern void zfs_zevent_post_cb(nvlist_t *nvl, nvlist_t *detector);
9798
extern int zfs_zevent_post(nvlist_t *, nvlist_t *, zevent_cb_t *);
9899
extern void zfs_zevent_drain_all(int *);
99-
extern int zfs_zevent_fd_hold(int, minor_t *, zfs_zevent_t **);
100-
extern void zfs_zevent_fd_rele(int);
100+
extern zfs_file_t *zfs_zevent_fd_hold(int, minor_t *, zfs_zevent_t **);
101+
extern void zfs_zevent_fd_rele(zfs_file_t *);
101102
extern int zfs_zevent_next(zfs_zevent_t *, nvlist_t **, uint64_t *, uint64_t *);
102103
extern int zfs_zevent_wait(zfs_zevent_t *);
103104
extern int zfs_zevent_seek(zfs_zevent_t *, uint64_t);

include/sys/zfs_file.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#ifndef _SYS_ZFS_FILE_H
2323
#define _SYS_ZFS_FILE_H
2424

25+
#include <sys/zfs_context.h>
26+
2527
#ifndef _KERNEL
2628
typedef struct zfs_file {
2729
int f_fd;
@@ -55,8 +57,8 @@ int zfs_file_fallocate(zfs_file_t *fp, int mode, loff_t offset, loff_t len);
5557
loff_t zfs_file_off(zfs_file_t *fp);
5658
int zfs_file_unlink(const char *);
5759

58-
int zfs_file_get(int fd, zfs_file_t **fp);
59-
void zfs_file_put(int fd);
60+
zfs_file_t *zfs_file_get(int fd);
61+
void zfs_file_put(zfs_file_t *fp);
6062
void *zfs_file_private(zfs_file_t *fp);
6163

6264
#endif /* _SYS_ZFS_FILE_H */

include/sys/zfs_ioctl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ typedef struct zfsdev_state {
566566
} zfsdev_state_t;
567567

568568
extern void *zfsdev_get_state(minor_t minor, enum zfsdev_state_type which);
569-
extern int zfsdev_getminor(int fd, minor_t *minorp);
569+
extern int zfsdev_getminor(zfs_file_t *fp, minor_t *minorp);
570570
extern minor_t zfsdev_minor_alloc(void);
571571

572572
extern uint_t zfs_fsyncer_key;

include/sys/zfs_onexit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ extern void zfs_onexit_destroy(zfs_onexit_t *zo);
5151

5252
#endif
5353

54-
extern int zfs_onexit_fd_hold(int fd, minor_t *minorp);
55-
extern void zfs_onexit_fd_rele(int fd);
54+
extern zfs_file_t *zfs_onexit_fd_hold(int fd, minor_t *minorp);
55+
extern void zfs_onexit_fd_rele(zfs_file_t *);
5656
extern int zfs_onexit_add_cb(minor_t minor, void (*func)(void *), void *data,
5757
uint64_t *action_handle);
5858

lib/libzpool/kernel.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -966,16 +966,16 @@ kmem_asprintf(const char *fmt, ...)
966966
}
967967

968968
/* ARGSUSED */
969-
int
969+
zfs_file_t *
970970
zfs_onexit_fd_hold(int fd, minor_t *minorp)
971971
{
972972
*minorp = 0;
973-
return (0);
973+
return (NULL);
974974
}
975975

976976
/* ARGSUSED */
977977
void
978-
zfs_onexit_fd_rele(int fd)
978+
zfs_onexit_fd_rele(zfs_file_t *fp)
979979
{
980980
}
981981

@@ -1385,28 +1385,26 @@ zfs_file_unlink(const char *path)
13851385
* Get reference to file pointer
13861386
*
13871387
* fd - input file descriptor
1388-
* fpp - pointer to file pointer
13891388
*
1390-
* Returns 0 on success EBADF on failure.
1389+
* Returns pointer to file struct or NULL.
13911390
* Unsupported in user space.
13921391
*/
1393-
int
1394-
zfs_file_get(int fd, zfs_file_t **fpp)
1392+
zfs_file_t *
1393+
zfs_file_get(int fd)
13951394
{
13961395
abort();
13971396

1398-
return (EOPNOTSUPP);
1397+
return (NULL);
13991398
}
1400-
14011399
/*
14021400
* Drop reference to file pointer
14031401
*
1404-
* fd - input file descriptor
1402+
* fp - pointer to file struct
14051403
*
14061404
* Unsupported in user space.
14071405
*/
14081406
void
1409-
zfs_file_put(int fd)
1407+
zfs_file_put(zfs_file_t *fp)
14101408
{
14111409
abort();
14121410
}

module/os/freebsd/zfs/zfs_file_os.c

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -241,28 +241,21 @@ zfs_file_fsync(zfs_file_t *fp, int flags)
241241
return (zfs_vop_fsync(fp->f_vnode));
242242
}
243243

244-
int
245-
zfs_file_get(int fd, zfs_file_t **fpp)
244+
zfs_file_t *
245+
zfs_file_get(int fd)
246246
{
247247
struct file *fp;
248248

249249
if (fget(curthread, fd, &cap_no_rights, &fp))
250-
return (SET_ERROR(EBADF));
250+
return (NULL);
251251

252-
*fpp = fp;
253-
return (0);
252+
return (fp);
254253
}
255254

256255
void
257-
zfs_file_put(int fd)
256+
zfs_file_put(zfs_file_t *fp)
258257
{
259-
struct file *fp;
260-
261-
/* No CAP_ rights required, as we're only releasing. */
262-
if (fget(curthread, fd, &cap_no_rights, &fp) == 0) {
263-
fdrop(fp, curthread);
264-
fdrop(fp, curthread);
265-
}
258+
fdrop(fp, curthread);
266259
}
267260

268261
loff_t

module/os/linux/zfs/zfs_file_os.c

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -405,36 +405,22 @@ zfs_file_unlink(const char *path)
405405
* Get reference to file pointer
406406
*
407407
* fd - input file descriptor
408-
* fpp - pointer to file pointer
409408
*
410-
* Returns 0 on success EBADF on failure.
409+
* Returns pointer to file struct or NULL
411410
*/
412-
int
413-
zfs_file_get(int fd, zfs_file_t **fpp)
411+
zfs_file_t *
412+
zfs_file_get(int fd)
414413
{
415-
zfs_file_t *fp;
416-
417-
fp = fget(fd);
418-
if (fp == NULL)
419-
return (EBADF);
420-
421-
*fpp = fp;
422-
423-
return (0);
414+
return (fget(fd));
424415
}
425416

426417
/*
427418
* Drop reference to file pointer
428419
*
429-
* fd - input file descriptor
420+
* fp - input file struct pointer
430421
*/
431422
void
432-
zfs_file_put(int fd)
423+
zfs_file_put(zfs_file_t *fp)
433424
{
434-
struct file *fp;
435-
436-
if ((fp = fget(fd)) != NULL) {
437-
fput(fp);
438-
fput(fp);
439-
}
425+
fput(fp);
440426
}

module/zfs/fm.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -586,25 +586,29 @@ zfs_zevent_minor_to_state(minor_t minor, zfs_zevent_t **ze)
586586
return (0);
587587
}
588588

589-
int
589+
zfs_file_t *
590590
zfs_zevent_fd_hold(int fd, minor_t *minorp, zfs_zevent_t **ze)
591591
{
592-
int error;
592+
zfs_file_t *fp = zfs_file_get(fd);
593+
if (fp == NULL)
594+
return (NULL);
593595

594-
error = zfsdev_getminor(fd, minorp);
596+
int error = zfsdev_getminor(fp, minorp);
595597
if (error == 0)
596598
error = zfs_zevent_minor_to_state(*minorp, ze);
597599

598-
if (error)
599-
zfs_zevent_fd_rele(fd);
600+
if (error) {
601+
zfs_zevent_fd_rele(fp);
602+
fp = NULL;
603+
}
600604

601-
return (error);
605+
return (fp);
602606
}
603607

604608
void
605-
zfs_zevent_fd_rele(int fd)
609+
zfs_zevent_fd_rele(zfs_file_t *fp)
606610
{
607-
zfs_file_put(fd);
611+
zfs_file_put(fp);
608612
}
609613

610614
/*

0 commit comments

Comments
 (0)