Skip to content

Commit d1ffcf9

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 openzfs#12299
1 parent 38a8bcb commit d1ffcf9

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,
@@ -95,8 +96,8 @@ extern void fm_fini(void);
9596
extern void zfs_zevent_post_cb(nvlist_t *nvl, nvlist_t *detector);
9697
extern int zfs_zevent_post(nvlist_t *, nvlist_t *, zevent_cb_t *);
9798
extern void zfs_zevent_drain_all(int *);
98-
extern int zfs_zevent_fd_hold(int, minor_t *, zfs_zevent_t **);
99-
extern void zfs_zevent_fd_rele(int);
99+
extern zfs_file_t *zfs_zevent_fd_hold(int, minor_t *, zfs_zevent_t **);
100+
extern void zfs_zevent_fd_rele(zfs_file_t *);
100101
extern int zfs_zevent_next(zfs_zevent_t *, nvlist_t **, uint64_t *, uint64_t *);
101102
extern int zfs_zevent_wait(zfs_zevent_t *);
102103
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
@@ -936,16 +936,16 @@ kmem_asprintf(const char *fmt, ...)
936936
}
937937

938938
/* ARGSUSED */
939-
int
939+
zfs_file_t *
940940
zfs_onexit_fd_hold(int fd, minor_t *minorp)
941941
{
942942
*minorp = 0;
943-
return (0);
943+
return (NULL);
944944
}
945945

946946
/* ARGSUSED */
947947
void
948-
zfs_onexit_fd_rele(int fd)
948+
zfs_onexit_fd_rele(zfs_file_t *fp)
949949
{
950950
}
951951

@@ -1355,28 +1355,26 @@ zfs_file_unlink(const char *path)
13551355
* Get reference to file pointer
13561356
*
13571357
* fd - input file descriptor
1358-
* fpp - pointer to file pointer
13591358
*
1360-
* Returns 0 on success EBADF on failure.
1359+
* Returns pointer to file struct or NULL.
13611360
* Unsupported in user space.
13621361
*/
1363-
int
1364-
zfs_file_get(int fd, zfs_file_t **fpp)
1362+
zfs_file_t *
1363+
zfs_file_get(int fd)
13651364
{
13661365
abort();
13671366

1368-
return (EOPNOTSUPP);
1367+
return (NULL);
13691368
}
1370-
13711369
/*
13721370
* Drop reference to file pointer
13731371
*
1374-
* fd - input file descriptor
1372+
* fp - pointer to file struct
13751373
*
13761374
* Unsupported in user space.
13771375
*/
13781376
void
1379-
zfs_file_put(int fd)
1377+
zfs_file_put(zfs_file_t *fp)
13801378
{
13811379
abort();
13821380
}

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
@@ -407,36 +407,22 @@ zfs_file_unlink(const char *path)
407407
* Get reference to file pointer
408408
*
409409
* fd - input file descriptor
410-
* fpp - pointer to file pointer
411410
*
412-
* Returns 0 on success EBADF on failure.
411+
* Returns pointer to file struct or NULL
413412
*/
414-
int
415-
zfs_file_get(int fd, zfs_file_t **fpp)
413+
zfs_file_t *
414+
zfs_file_get(int fd)
416415
{
417-
zfs_file_t *fp;
418-
419-
fp = fget(fd);
420-
if (fp == NULL)
421-
return (EBADF);
422-
423-
*fpp = fp;
424-
425-
return (0);
416+
return (fget(fd));
426417
}
427418

428419
/*
429420
* Drop reference to file pointer
430421
*
431-
* fd - input file descriptor
422+
* fp - input file struct pointer
432423
*/
433424
void
434-
zfs_file_put(int fd)
425+
zfs_file_put(zfs_file_t *fp)
435426
{
436-
struct file *fp;
437-
438-
if ((fp = fget(fd)) != NULL) {
439-
fput(fp);
440-
fput(fp);
441-
}
427+
fput(fp);
442428
}

module/zfs/fm.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -278,25 +278,29 @@ zfs_zevent_minor_to_state(minor_t minor, zfs_zevent_t **ze)
278278
return (0);
279279
}
280280

281-
int
281+
zfs_file_t *
282282
zfs_zevent_fd_hold(int fd, minor_t *minorp, zfs_zevent_t **ze)
283283
{
284-
int error;
284+
zfs_file_t *fp = zfs_file_get(fd);
285+
if (fp == NULL)
286+
return (NULL);
285287

286-
error = zfsdev_getminor(fd, minorp);
288+
int error = zfsdev_getminor(fp, minorp);
287289
if (error == 0)
288290
error = zfs_zevent_minor_to_state(*minorp, ze);
289291

290-
if (error)
291-
zfs_zevent_fd_rele(fd);
292+
if (error) {
293+
zfs_zevent_fd_rele(fp);
294+
fp = NULL;
295+
}
292296

293-
return (error);
297+
return (fp);
294298
}
295299

296300
void
297-
zfs_zevent_fd_rele(int fd)
301+
zfs_zevent_fd_rele(zfs_file_t *fp)
298302
{
299-
zfs_file_put(fd);
303+
zfs_file_put(fp);
300304
}
301305

302306
/*

0 commit comments

Comments
 (0)