MFV r302646:

6980 6902 causes zfs send to break due to 32-bit/64-bit struct mismatch

illumos/illumos-gate@ea4a67f462
https://github.com/illumos/illumos-gate/commit/ea4a67f462de0a39a9adea8197bcdef84
9de5371

https://www.illumos.org/issues/6980
  doing zfs send -i snap1 snap2 >testfile results in
  internal error: Invalid argument
  Abort (core dumped)

Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: George Wilson <george.wilson@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Author: Matthew Ahrens <mahrens@delphix.com>
This commit is contained in:
Alexander Motin 2016-09-01 14:06:30 +00:00
commit 1c7d88abed
4 changed files with 156 additions and 3 deletions

View File

@ -55,8 +55,52 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
zfs_cmd_zcmd_t *zcmd_c;
zfs_cmd_edbp_t *edbp_c;
zfs_cmd_resume_t *resume_c;
zfs_cmd_inlanes_t *inlanes_c;
switch (cflag) {
case ZFS_CMD_COMPAT_INLANES:
inlanes_c = (void *)addr;
/* zc */
strlcpy(zc->zc_name, inlanes_c->zc_name, MAXPATHLEN);
strlcpy(zc->zc_value, inlanes_c->zc_value, MAXPATHLEN * 2);
strlcpy(zc->zc_string, inlanes_c->zc_string, MAXPATHLEN);
#define FIELD_COPY(field) zc->field = inlanes_c->field
FIELD_COPY(zc_nvlist_src);
FIELD_COPY(zc_nvlist_src_size);
FIELD_COPY(zc_nvlist_dst);
FIELD_COPY(zc_nvlist_dst_size);
FIELD_COPY(zc_nvlist_dst_filled);
FIELD_COPY(zc_pad2);
FIELD_COPY(zc_history);
FIELD_COPY(zc_guid);
FIELD_COPY(zc_nvlist_conf);
FIELD_COPY(zc_nvlist_conf_size);
FIELD_COPY(zc_cookie);
FIELD_COPY(zc_objset_type);
FIELD_COPY(zc_perm_action);
FIELD_COPY(zc_history_len);
FIELD_COPY(zc_history_offset);
FIELD_COPY(zc_obj);
FIELD_COPY(zc_iflags);
FIELD_COPY(zc_share);
FIELD_COPY(zc_jailid);
FIELD_COPY(zc_objset_stats);
FIELD_COPY(zc_begin_record);
FIELD_COPY(zc_inject_record);
FIELD_COPY(zc_defer_destroy);
FIELD_COPY(zc_flags);
FIELD_COPY(zc_action_handle);
FIELD_COPY(zc_cleanup_fd);
FIELD_COPY(zc_simple);
FIELD_COPY(zc_resumable);
FIELD_COPY(zc_sendobj);
FIELD_COPY(zc_fromobj);
FIELD_COPY(zc_createtxg);
FIELD_COPY(zc_stat);
#undef FIELD_COPY
break;
case ZFS_CMD_COMPAT_RESUME:
resume_c = (void *)addr;
/* zc */
@ -434,8 +478,50 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int request,
zfs_cmd_zcmd_t *zcmd_c;
zfs_cmd_edbp_t *edbp_c;
zfs_cmd_resume_t *resume_c;
zfs_cmd_inlanes_t *inlanes_c;
switch (cflag) {
case ZFS_CMD_COMPAT_INLANES:
inlanes_c = (void *)addr;
strlcpy(inlanes_c->zc_name, zc->zc_name, MAXPATHLEN);
strlcpy(inlanes_c->zc_value, zc->zc_value, MAXPATHLEN * 2);
strlcpy(inlanes_c->zc_string, zc->zc_string, MAXPATHLEN);
#define FIELD_COPY(field) inlanes_c->field = zc->field
FIELD_COPY(zc_nvlist_src);
FIELD_COPY(zc_nvlist_src_size);
FIELD_COPY(zc_nvlist_dst);
FIELD_COPY(zc_nvlist_dst_size);
FIELD_COPY(zc_nvlist_dst_filled);
FIELD_COPY(zc_pad2);
FIELD_COPY(zc_history);
FIELD_COPY(zc_guid);
FIELD_COPY(zc_nvlist_conf);
FIELD_COPY(zc_nvlist_conf_size);
FIELD_COPY(zc_cookie);
FIELD_COPY(zc_objset_type);
FIELD_COPY(zc_perm_action);
FIELD_COPY(zc_history_len);
FIELD_COPY(zc_history_offset);
FIELD_COPY(zc_obj);
FIELD_COPY(zc_iflags);
FIELD_COPY(zc_share);
FIELD_COPY(zc_jailid);
FIELD_COPY(zc_objset_stats);
FIELD_COPY(zc_begin_record);
FIELD_COPY(zc_inject_record);
FIELD_COPY(zc_defer_destroy);
FIELD_COPY(zc_flags);
FIELD_COPY(zc_action_handle);
FIELD_COPY(zc_cleanup_fd);
FIELD_COPY(zc_simple);
FIELD_COPY(zc_sendobj);
FIELD_COPY(zc_fromobj);
FIELD_COPY(zc_createtxg);
FIELD_COPY(zc_stat);
#undef FIELD_COPY
break;
case ZFS_CMD_COMPAT_RESUME:
resume_c = (void *)addr;
strlcpy(resume_c->zc_name, zc->zc_name, MAXPATHLEN);
@ -987,6 +1073,12 @@ zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
zp.zfs_cmd_size = sizeof(zfs_cmd_t);
zp.zfs_ioctl_version = ZFS_IOCVER_CURRENT;
return (ioctl(fd, ncmd, &zp));
case ZFS_CMD_COMPAT_INLANES:
ncmd = _IOWR('Z', request, struct zfs_iocparm);
zp.zfs_cmd = (uint64_t)zc;
zp.zfs_cmd_size = sizeof(zfs_cmd_inlanes_t);
zp.zfs_ioctl_version = ZFS_IOCVER_INLANES;
return (ioctl(fd, ncmd, &zp));
case ZFS_CMD_COMPAT_RESUME:
ncmd = _IOWR('Z', request, struct zfs_iocparm);
zp.zfs_cmd = (uint64_t)zc;
@ -1104,7 +1196,7 @@ zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t * innvl, const int vec,
if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
cflag == ZFS_CMD_COMPAT_RESUME)
cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
goto out;
switch (vec) {
@ -1257,7 +1349,7 @@ zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t * outnvl, const int vec,
if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
cflag == ZFS_CMD_COMPAT_RESUME)
cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
return (outnvl);
switch (vec) {

View File

@ -54,7 +54,8 @@ extern "C" {
#define ZFS_IOCVER_EDBP 4
#define ZFS_IOCVER_RESUME 5
#define ZFS_IOCVER_INLANES 6
#define ZFS_IOCVER_CURRENT ZFS_IOCVER_INLANES
#define ZFS_IOCVER_PAD 7
#define ZFS_IOCVER_CURRENT ZFS_IOCVER_PAD
/* compatibility conversion flag */
#define ZFS_CMD_COMPAT_NONE 0
@ -65,6 +66,7 @@ extern "C" {
#define ZFS_CMD_COMPAT_ZCMD 5
#define ZFS_CMD_COMPAT_EDBP 6
#define ZFS_CMD_COMPAT_RESUME 7
#define ZFS_CMD_COMPAT_INLANES 8
#define ZFS_IOC_COMPAT_PASS 254
#define ZFS_IOC_COMPAT_FAIL 255
@ -355,6 +357,49 @@ typedef struct zfs_cmd_resume {
zfs_stat_t zc_stat;
} zfs_cmd_resume_t;
typedef struct zfs_cmd_inlanes {
char zc_name[MAXPATHLEN]; /* name of pool or dataset */
uint64_t zc_nvlist_src; /* really (char *) */
uint64_t zc_nvlist_src_size;
uint64_t zc_nvlist_dst; /* really (char *) */
uint64_t zc_nvlist_dst_size;
boolean_t zc_nvlist_dst_filled; /* put an nvlist in dst? */
int zc_pad2;
/*
* The following members are for legacy ioctls which haven't been
* converted to the new method.
*/
uint64_t zc_history; /* really (char *) */
char zc_value[MAXPATHLEN * 2];
char zc_string[MAXNAMELEN];
uint64_t zc_guid;
uint64_t zc_nvlist_conf; /* really (char *) */
uint64_t zc_nvlist_conf_size;
uint64_t zc_cookie;
uint64_t zc_objset_type;
uint64_t zc_perm_action;
uint64_t zc_history_len;
uint64_t zc_history_offset;
uint64_t zc_obj;
uint64_t zc_iflags; /* internal to zfs(7fs) */
zfs_share_t zc_share;
uint64_t zc_jailid;
dmu_objset_stats_t zc_objset_stats;
dmu_replay_record_t zc_begin_record;
zinject_record_t zc_inject_record;
uint32_t zc_defer_destroy;
uint32_t zc_flags;
uint64_t zc_action_handle;
int zc_cleanup_fd;
uint8_t zc_simple;
boolean_t zc_resumable;
uint64_t zc_sendobj;
uint64_t zc_fromobj;
uint64_t zc_createtxg;
zfs_stat_t zc_stat;
} zfs_cmd_inlanes_t;
#ifdef _KERNEL
unsigned static long zfs_ioctl_v15_to_v28[] = {
0, /* 0 ZFS_IOC_POOL_CREATE */

View File

@ -353,6 +353,12 @@ typedef enum zfs_case {
ZFS_CASE_MIXED
} zfs_case_t;
/*
* Note: this struct must have the same layout in 32-bit and 64-bit, so
* that 32-bit processes (like /sbin/zfs) can pass it to the 64-bit
* kernel. Therefore, we add padding to it so that no "hidden" padding
* is automatically added on 64-bit (but not on 32-bit).
*/
typedef struct zfs_cmd {
char zc_name[MAXPATHLEN]; /* name of pool or dataset */
uint64_t zc_nvlist_src; /* really (char *) */
@ -389,7 +395,9 @@ typedef struct zfs_cmd {
uint64_t zc_action_handle;
int zc_cleanup_fd;
uint8_t zc_simple;
uint8_t zc_pad3[3];
boolean_t zc_resumable;
uint32_t zc_pad4;
uint64_t zc_sendobj;
uint64_t zc_fromobj;
uint64_t zc_createtxg;

View File

@ -6252,6 +6252,14 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
goto out;
}
break;
case ZFS_IOCVER_INLANES:
if (zc_iocparm->zfs_cmd_size != sizeof(zfs_cmd_inlanes_t)) {
error = SET_ERROR(EFAULT);
goto out;
}
compat = B_TRUE;
cflag = ZFS_CMD_COMPAT_INLANES;
break;
case ZFS_IOCVER_RESUME:
if (zc_iocparm->zfs_cmd_size != sizeof(zfs_cmd_resume_t)) {
error = SET_ERROR(EFAULT);