* [PULL 1/5] linux-user: Split out do_getdents, do_getdents64
2021-11-22 8:23 [PULL 0/5] Linux user for 6.2 patches Laurent Vivier
@ 2021-11-22 8:23 ` Laurent Vivier
2021-11-22 8:23 ` [PULL 2/5] linux-user: Always use flexible arrays for dirent d_name Laurent Vivier
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Laurent Vivier @ 2021-11-22 8:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Philippe Mathieu-Daudé, Laurent Vivier
From: Richard Henderson <richard.henderson@linaro.org>
Retain all 3 implementations of getdents for now.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <20211114103539.298686-2-richard.henderson@linaro.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
linux-user/syscall.c | 325 +++++++++++++++++++++++--------------------
1 file changed, 172 insertions(+), 153 deletions(-)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 544f5b662ffe..a2f605dec4ca 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8137,6 +8137,176 @@ static int host_to_target_cpu_mask(const unsigned long *host_mask,
return 0;
}
+#ifdef TARGET_NR_getdents
+static int do_getdents(abi_long arg1, abi_long arg2, abi_long arg3)
+{
+ int ret;
+
+#ifdef EMULATE_GETDENTS_WITH_GETDENTS
+# if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64
+ struct target_dirent *target_dirp;
+ struct linux_dirent *dirp;
+ abi_long count = arg3;
+
+ dirp = g_try_malloc(count);
+ if (!dirp) {
+ return -TARGET_ENOMEM;
+ }
+
+ ret = get_errno(sys_getdents(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct linux_dirent *de;
+ struct target_dirent *tde;
+ int len = ret;
+ int reclen, treclen;
+ int count1, tnamelen;
+
+ count1 = 0;
+ de = dirp;
+ target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
+ if (!target_dirp) {
+ return -TARGET_EFAULT;
+ }
+ tde = target_dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ tnamelen = reclen - offsetof(struct linux_dirent, d_name);
+ assert(tnamelen >= 0);
+ treclen = tnamelen + offsetof(struct target_dirent, d_name);
+ assert(count1 + treclen <= count);
+ tde->d_reclen = tswap16(treclen);
+ tde->d_ino = tswapal(de->d_ino);
+ tde->d_off = tswapal(de->d_off);
+ memcpy(tde->d_name, de->d_name, tnamelen);
+ de = (struct linux_dirent *)((char *)de + reclen);
+ len -= reclen;
+ tde = (struct target_dirent *)((char *)tde + treclen);
+ count1 += treclen;
+ }
+ ret = count1;
+ unlock_user(target_dirp, arg2, ret);
+ }
+ g_free(dirp);
+# else
+ struct linux_dirent *dirp;
+ abi_long count = arg3;
+
+ dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
+ if (!dirp) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(sys_getdents(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct linux_dirent *de;
+ int len = ret;
+ int reclen;
+ de = dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ if (reclen > len) {
+ break;
+ }
+ de->d_reclen = tswap16(reclen);
+ tswapls(&de->d_ino);
+ tswapls(&de->d_off);
+ de = (struct linux_dirent *)((char *)de + reclen);
+ len -= reclen;
+ }
+ }
+ unlock_user(dirp, arg2, ret);
+# endif
+#else
+ /* Implement getdents in terms of getdents64 */
+ struct linux_dirent64 *dirp;
+ abi_long count = arg3;
+
+ dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
+ if (!dirp) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(sys_getdents64(arg1, dirp, count));
+ if (!is_error(ret)) {
+ /*
+ * Convert the dirent64 structs to target dirent. We do this
+ * in-place, since we can guarantee that a target_dirent is no
+ * larger than a dirent64; however this means we have to be
+ * careful to read everything before writing in the new format.
+ */
+ struct linux_dirent64 *de;
+ struct target_dirent *tde;
+ int len = ret;
+ int tlen = 0;
+
+ de = dirp;
+ tde = (struct target_dirent *)dirp;
+ while (len > 0) {
+ int namelen, treclen;
+ int reclen = de->d_reclen;
+ uint64_t ino = de->d_ino;
+ int64_t off = de->d_off;
+ uint8_t type = de->d_type;
+
+ namelen = strlen(de->d_name);
+ treclen = offsetof(struct target_dirent, d_name) + namelen + 2;
+ treclen = QEMU_ALIGN_UP(treclen, sizeof(abi_long));
+
+ memmove(tde->d_name, de->d_name, namelen + 1);
+ tde->d_ino = tswapal(ino);
+ tde->d_off = tswapal(off);
+ tde->d_reclen = tswap16(treclen);
+ /*
+ * The target_dirent type is in what was formerly a padding
+ * byte at the end of the structure:
+ */
+ *(((char *)tde) + treclen - 1) = type;
+
+ de = (struct linux_dirent64 *)((char *)de + reclen);
+ tde = (struct target_dirent *)((char *)tde + treclen);
+ len -= reclen;
+ tlen += treclen;
+ }
+ ret = tlen;
+ }
+ unlock_user(dirp, arg2, ret);
+#endif
+ return ret;
+}
+#endif /* TARGET_NR_getdents */
+
+#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
+static int do_getdents64(abi_long arg1, abi_long arg2, abi_long arg3)
+{
+ struct linux_dirent64 *dirp;
+ abi_long count = arg3;
+ int ret;
+
+ dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
+ if (!dirp) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(sys_getdents64(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct linux_dirent64 *de;
+ int len = ret;
+ int reclen;
+ de = dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ if (reclen > len) {
+ break;
+ }
+ de->d_reclen = tswap16(reclen);
+ tswap64s((uint64_t *)&de->d_ino);
+ tswap64s((uint64_t *)&de->d_off);
+ de = (struct linux_dirent64 *)((char *)de + reclen);
+ len -= reclen;
+ }
+ }
+ unlock_user(dirp, arg2, ret);
+ return ret;
+}
+#endif /* TARGET_NR_getdents64 */
+
#if defined(TARGET_NR_pivot_root) && defined(__NR_pivot_root)
_syscall2(int, pivot_root, const char *, new_root, const char *, put_old)
#endif
@@ -10227,162 +10397,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
#endif
#ifdef TARGET_NR_getdents
case TARGET_NR_getdents:
-#ifdef EMULATE_GETDENTS_WITH_GETDENTS
-#if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64
- {
- struct target_dirent *target_dirp;
- struct linux_dirent *dirp;
- abi_long count = arg3;
-
- dirp = g_try_malloc(count);
- if (!dirp) {
- return -TARGET_ENOMEM;
- }
-
- ret = get_errno(sys_getdents(arg1, dirp, count));
- if (!is_error(ret)) {
- struct linux_dirent *de;
- struct target_dirent *tde;
- int len = ret;
- int reclen, treclen;
- int count1, tnamelen;
-
- count1 = 0;
- de = dirp;
- if (!(target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
- return -TARGET_EFAULT;
- tde = target_dirp;
- while (len > 0) {
- reclen = de->d_reclen;
- tnamelen = reclen - offsetof(struct linux_dirent, d_name);
- assert(tnamelen >= 0);
- treclen = tnamelen + offsetof(struct target_dirent, d_name);
- assert(count1 + treclen <= count);
- tde->d_reclen = tswap16(treclen);
- tde->d_ino = tswapal(de->d_ino);
- tde->d_off = tswapal(de->d_off);
- memcpy(tde->d_name, de->d_name, tnamelen);
- de = (struct linux_dirent *)((char *)de + reclen);
- len -= reclen;
- tde = (struct target_dirent *)((char *)tde + treclen);
- count1 += treclen;
- }
- ret = count1;
- unlock_user(target_dirp, arg2, ret);
- }
- g_free(dirp);
- }
-#else
- {
- struct linux_dirent *dirp;
- abi_long count = arg3;
-
- if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
- return -TARGET_EFAULT;
- ret = get_errno(sys_getdents(arg1, dirp, count));
- if (!is_error(ret)) {
- struct linux_dirent *de;
- int len = ret;
- int reclen;
- de = dirp;
- while (len > 0) {
- reclen = de->d_reclen;
- if (reclen > len)
- break;
- de->d_reclen = tswap16(reclen);
- tswapls(&de->d_ino);
- tswapls(&de->d_off);
- de = (struct linux_dirent *)((char *)de + reclen);
- len -= reclen;
- }
- }
- unlock_user(dirp, arg2, ret);
- }
-#endif
-#else
- /* Implement getdents in terms of getdents64 */
- {
- struct linux_dirent64 *dirp;
- abi_long count = arg3;
-
- dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
- if (!dirp) {
- return -TARGET_EFAULT;
- }
- ret = get_errno(sys_getdents64(arg1, dirp, count));
- if (!is_error(ret)) {
- /* Convert the dirent64 structs to target dirent. We do this
- * in-place, since we can guarantee that a target_dirent is no
- * larger than a dirent64; however this means we have to be
- * careful to read everything before writing in the new format.
- */
- struct linux_dirent64 *de;
- struct target_dirent *tde;
- int len = ret;
- int tlen = 0;
-
- de = dirp;
- tde = (struct target_dirent *)dirp;
- while (len > 0) {
- int namelen, treclen;
- int reclen = de->d_reclen;
- uint64_t ino = de->d_ino;
- int64_t off = de->d_off;
- uint8_t type = de->d_type;
-
- namelen = strlen(de->d_name);
- treclen = offsetof(struct target_dirent, d_name)
- + namelen + 2;
- treclen = QEMU_ALIGN_UP(treclen, sizeof(abi_long));
-
- memmove(tde->d_name, de->d_name, namelen + 1);
- tde->d_ino = tswapal(ino);
- tde->d_off = tswapal(off);
- tde->d_reclen = tswap16(treclen);
- /* The target_dirent type is in what was formerly a padding
- * byte at the end of the structure:
- */
- *(((char *)tde) + treclen - 1) = type;
-
- de = (struct linux_dirent64 *)((char *)de + reclen);
- tde = (struct target_dirent *)((char *)tde + treclen);
- len -= reclen;
- tlen += treclen;
- }
- ret = tlen;
- }
- unlock_user(dirp, arg2, ret);
- }
-#endif
- return ret;
+ return do_getdents(arg1, arg2, arg3);
#endif /* TARGET_NR_getdents */
#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
case TARGET_NR_getdents64:
- {
- struct linux_dirent64 *dirp;
- abi_long count = arg3;
- if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
- return -TARGET_EFAULT;
- ret = get_errno(sys_getdents64(arg1, dirp, count));
- if (!is_error(ret)) {
- struct linux_dirent64 *de;
- int len = ret;
- int reclen;
- de = dirp;
- while (len > 0) {
- reclen = de->d_reclen;
- if (reclen > len)
- break;
- de->d_reclen = tswap16(reclen);
- tswap64s((uint64_t *)&de->d_ino);
- tswap64s((uint64_t *)&de->d_off);
- de = (struct linux_dirent64 *)((char *)de + reclen);
- len -= reclen;
- }
- }
- unlock_user(dirp, arg2, ret);
- }
- return ret;
+ return do_getdents64(arg1, arg2, arg3);
#endif /* TARGET_NR_getdents64 */
#if defined(TARGET_NR__newselect)
case TARGET_NR__newselect:
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PULL 2/5] linux-user: Always use flexible arrays for dirent d_name
2021-11-22 8:23 [PULL 0/5] Linux user for 6.2 patches Laurent Vivier
2021-11-22 8:23 ` [PULL 1/5] linux-user: Split out do_getdents, do_getdents64 Laurent Vivier
@ 2021-11-22 8:23 ` Laurent Vivier
2021-11-22 8:23 ` [PULL 3/5] linux-user: Fix member types of target_dirent64 Laurent Vivier
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Laurent Vivier @ 2021-11-22 8:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Philippe Mathieu-Daudé, Laurent Vivier
From: Richard Henderson <richard.henderson@linaro.org>
We currently use a flexible array member for target_dirent,
but use incorrectly fixed length arrays for target_dirent64,
linux_dirent and linux_dirent64.
This requires that we adjust the definition of the VFAT READDIR
ioctls which hard-code the 256 namelen size into the ioctl constant.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <20211114103539.298686-3-richard.henderson@linaro.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
linux-user/syscall.c | 6 ++++--
linux-user/syscall_defs.h | 6 +++---
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index a2f605dec4ca..499415ad81b8 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -197,8 +197,10 @@
//#define DEBUG_ERESTARTSYS
//#include <linux/msdos_fs.h>
-#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct linux_dirent [2])
-#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct linux_dirent [2])
+#define VFAT_IOCTL_READDIR_BOTH \
+ _IOC(_IOC_READ, 'r', 1, (sizeof(struct linux_dirent) + 256) * 2)
+#define VFAT_IOCTL_READDIR_SHORT \
+ _IOC(_IOC_READ, 'r', 2, (sizeof(struct linux_dirent) + 256) * 2)
#undef _syscall0
#undef _syscall1
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index a5ce487dcc38..98b09ee6d656 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -441,7 +441,7 @@ struct target_dirent64 {
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
- char d_name[256];
+ char d_name[];
};
@@ -2714,7 +2714,7 @@ struct linux_dirent {
long d_ino;
unsigned long d_off;
unsigned short d_reclen;
- char d_name[256]; /* We must not include limits.h! */
+ char d_name[];
};
struct linux_dirent64 {
@@ -2722,7 +2722,7 @@ struct linux_dirent64 {
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
- char d_name[256];
+ char d_name[];
};
struct target_mq_attr {
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PULL 3/5] linux-user: Fix member types of target_dirent64
2021-11-22 8:23 [PULL 0/5] Linux user for 6.2 patches Laurent Vivier
2021-11-22 8:23 ` [PULL 1/5] linux-user: Split out do_getdents, do_getdents64 Laurent Vivier
2021-11-22 8:23 ` [PULL 2/5] linux-user: Always use flexible arrays for dirent d_name Laurent Vivier
@ 2021-11-22 8:23 ` Laurent Vivier
2021-11-22 8:23 ` [PULL 4/5] linux-user: Rewrite do_getdents, do_getdents64 Laurent Vivier
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Laurent Vivier @ 2021-11-22 8:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Philippe Mathieu-Daudé, Laurent Vivier
From: Richard Henderson <richard.henderson@linaro.org>
The host uint64_t (etc) does not have the correct
alignment constraint as the guest: use abi_* types.
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <20211114103539.298686-4-richard.henderson@linaro.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
linux-user/syscall_defs.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 98b09ee6d656..41aaafbac12c 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -437,9 +437,9 @@ struct target_dirent {
};
struct target_dirent64 {
- uint64_t d_ino;
- int64_t d_off;
- unsigned short d_reclen;
+ abi_ullong d_ino;
+ abi_llong d_off;
+ abi_ushort d_reclen;
unsigned char d_type;
char d_name[];
};
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PULL 4/5] linux-user: Rewrite do_getdents, do_getdents64
2021-11-22 8:23 [PULL 0/5] Linux user for 6.2 patches Laurent Vivier
` (2 preceding siblings ...)
2021-11-22 8:23 ` [PULL 3/5] linux-user: Fix member types of target_dirent64 Laurent Vivier
@ 2021-11-22 8:23 ` Laurent Vivier
2021-11-22 8:23 ` [PULL 5/5] linux-user: fix Coverity CID 1464101 Laurent Vivier
2021-11-22 11:13 ` [PULL 0/5] Linux user for 6.2 patches Richard Henderson
5 siblings, 0 replies; 7+ messages in thread
From: Laurent Vivier @ 2021-11-22 8:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Richard Henderson, Philippe Mathieu-Daudé, Laurent Vivier
From: Richard Henderson <richard.henderson@linaro.org>
Always allocate host storage; this ensures that the struct
is sufficiently aligned for the host. Merge the three host
implementations of getdents via a few ifdefs. Utilize the
same method for do_getdents64.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/704
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <20211114103539.298686-5-richard.henderson@linaro.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
linux-user/syscall.c | 261 ++++++++++++++++++++-----------------------
1 file changed, 122 insertions(+), 139 deletions(-)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 499415ad81b8..f1cfcc810486 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8140,172 +8140,155 @@ static int host_to_target_cpu_mask(const unsigned long *host_mask,
}
#ifdef TARGET_NR_getdents
-static int do_getdents(abi_long arg1, abi_long arg2, abi_long arg3)
+static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count)
{
- int ret;
+ g_autofree void *hdirp = NULL;
+ void *tdirp;
+ int hlen, hoff, toff;
+ int hreclen, treclen;
+ off64_t prev_diroff = 0;
+
+ hdirp = g_try_malloc(count);
+ if (!hdirp) {
+ return -TARGET_ENOMEM;
+ }
#ifdef EMULATE_GETDENTS_WITH_GETDENTS
-# if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64
- struct target_dirent *target_dirp;
- struct linux_dirent *dirp;
- abi_long count = arg3;
+ hlen = sys_getdents(dirfd, hdirp, count);
+#else
+ hlen = sys_getdents64(dirfd, hdirp, count);
+#endif
- dirp = g_try_malloc(count);
- if (!dirp) {
- return -TARGET_ENOMEM;
+ hlen = get_errno(hlen);
+ if (is_error(hlen)) {
+ return hlen;
}
- ret = get_errno(sys_getdents(arg1, dirp, count));
- if (!is_error(ret)) {
- struct linux_dirent *de;
- struct target_dirent *tde;
- int len = ret;
- int reclen, treclen;
- int count1, tnamelen;
-
- count1 = 0;
- de = dirp;
- target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
- if (!target_dirp) {
- return -TARGET_EFAULT;
- }
- tde = target_dirp;
- while (len > 0) {
- reclen = de->d_reclen;
- tnamelen = reclen - offsetof(struct linux_dirent, d_name);
- assert(tnamelen >= 0);
- treclen = tnamelen + offsetof(struct target_dirent, d_name);
- assert(count1 + treclen <= count);
- tde->d_reclen = tswap16(treclen);
- tde->d_ino = tswapal(de->d_ino);
- tde->d_off = tswapal(de->d_off);
- memcpy(tde->d_name, de->d_name, tnamelen);
- de = (struct linux_dirent *)((char *)de + reclen);
- len -= reclen;
- tde = (struct target_dirent *)((char *)tde + treclen);
- count1 += treclen;
- }
- ret = count1;
- unlock_user(target_dirp, arg2, ret);
- }
- g_free(dirp);
-# else
- struct linux_dirent *dirp;
- abi_long count = arg3;
-
- dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
- if (!dirp) {
+ tdirp = lock_user(VERIFY_WRITE, arg2, count, 0);
+ if (!tdirp) {
return -TARGET_EFAULT;
}
- ret = get_errno(sys_getdents(arg1, dirp, count));
- if (!is_error(ret)) {
- struct linux_dirent *de;
- int len = ret;
- int reclen;
- de = dirp;
- while (len > 0) {
- reclen = de->d_reclen;
- if (reclen > len) {
- break;
- }
- de->d_reclen = tswap16(reclen);
- tswapls(&de->d_ino);
- tswapls(&de->d_off);
- de = (struct linux_dirent *)((char *)de + reclen);
- len -= reclen;
- }
- }
- unlock_user(dirp, arg2, ret);
-# endif
+
+ for (hoff = toff = 0; hoff < hlen; hoff += hreclen, toff += treclen) {
+#ifdef EMULATE_GETDENTS_WITH_GETDENTS
+ struct linux_dirent *hde = hdirp + hoff;
#else
- /* Implement getdents in terms of getdents64 */
- struct linux_dirent64 *dirp;
- abi_long count = arg3;
+ struct linux_dirent64 *hde = hdirp + hoff;
+#endif
+ struct target_dirent *tde = tdirp + toff;
+ int namelen;
+ uint8_t type;
- dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
- if (!dirp) {
- return -TARGET_EFAULT;
- }
- ret = get_errno(sys_getdents64(arg1, dirp, count));
- if (!is_error(ret)) {
- /*
- * Convert the dirent64 structs to target dirent. We do this
- * in-place, since we can guarantee that a target_dirent is no
- * larger than a dirent64; however this means we have to be
- * careful to read everything before writing in the new format.
- */
- struct linux_dirent64 *de;
- struct target_dirent *tde;
- int len = ret;
- int tlen = 0;
+ namelen = strlen(hde->d_name);
+ hreclen = hde->d_reclen;
+ treclen = offsetof(struct target_dirent, d_name) + namelen + 2;
+ treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent));
- de = dirp;
- tde = (struct target_dirent *)dirp;
- while (len > 0) {
- int namelen, treclen;
- int reclen = de->d_reclen;
- uint64_t ino = de->d_ino;
- int64_t off = de->d_off;
- uint8_t type = de->d_type;
-
- namelen = strlen(de->d_name);
- treclen = offsetof(struct target_dirent, d_name) + namelen + 2;
- treclen = QEMU_ALIGN_UP(treclen, sizeof(abi_long));
-
- memmove(tde->d_name, de->d_name, namelen + 1);
- tde->d_ino = tswapal(ino);
- tde->d_off = tswapal(off);
- tde->d_reclen = tswap16(treclen);
+ if (toff + treclen > count) {
/*
- * The target_dirent type is in what was formerly a padding
- * byte at the end of the structure:
+ * If the host struct is smaller than the target struct, or
+ * requires less alignment and thus packs into less space,
+ * then the host can return more entries than we can pass
+ * on to the guest.
*/
- *(((char *)tde) + treclen - 1) = type;
-
- de = (struct linux_dirent64 *)((char *)de + reclen);
- tde = (struct target_dirent *)((char *)tde + treclen);
- len -= reclen;
- tlen += treclen;
+ if (toff == 0) {
+ toff = -TARGET_EINVAL; /* result buffer is too small */
+ break;
+ }
+ /*
+ * Return what we have, resetting the file pointer to the
+ * location of the first record not returned.
+ */
+ lseek64(dirfd, prev_diroff, SEEK_SET);
+ break;
}
- ret = tlen;
- }
- unlock_user(dirp, arg2, ret);
+
+ prev_diroff = hde->d_off;
+ tde->d_ino = tswapal(hde->d_ino);
+ tde->d_off = tswapal(hde->d_off);
+ tde->d_reclen = tswap16(treclen);
+ memcpy(tde->d_name, hde->d_name, namelen + 1);
+
+ /*
+ * The getdents type is in what was formerly a padding byte at the
+ * end of the structure.
+ */
+#ifdef EMULATE_GETDENTS_WITH_GETDENTS
+ type = *((uint8_t *)hde + hreclen - 1);
+#else
+ type = hde->d_type;
#endif
- return ret;
+ *((uint8_t *)tde + treclen - 1) = type;
+ }
+
+ unlock_user(tdirp, arg2, toff);
+ return toff;
}
#endif /* TARGET_NR_getdents */
#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
-static int do_getdents64(abi_long arg1, abi_long arg2, abi_long arg3)
+static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)
{
- struct linux_dirent64 *dirp;
- abi_long count = arg3;
- int ret;
+ g_autofree void *hdirp = NULL;
+ void *tdirp;
+ int hlen, hoff, toff;
+ int hreclen, treclen;
+ off64_t prev_diroff = 0;
+
+ hdirp = g_try_malloc(count);
+ if (!hdirp) {
+ return -TARGET_ENOMEM;
+ }
- dirp = lock_user(VERIFY_WRITE, arg2, count, 0);
- if (!dirp) {
+ hlen = get_errno(sys_getdents64(dirfd, hdirp, count));
+ if (is_error(hlen)) {
+ return hlen;
+ }
+
+ tdirp = lock_user(VERIFY_WRITE, arg2, count, 0);
+ if (!tdirp) {
return -TARGET_EFAULT;
}
- ret = get_errno(sys_getdents64(arg1, dirp, count));
- if (!is_error(ret)) {
- struct linux_dirent64 *de;
- int len = ret;
- int reclen;
- de = dirp;
- while (len > 0) {
- reclen = de->d_reclen;
- if (reclen > len) {
+
+ for (hoff = toff = 0; hoff < hlen; hoff += hreclen, toff += treclen) {
+ struct linux_dirent64 *hde = hdirp + hoff;
+ struct target_dirent64 *tde = tdirp + toff;
+ int namelen;
+
+ namelen = strlen(hde->d_name) + 1;
+ hreclen = hde->d_reclen;
+ treclen = offsetof(struct target_dirent64, d_name) + namelen;
+ treclen = QEMU_ALIGN_UP(treclen, __alignof(struct target_dirent64));
+
+ if (toff + treclen > count) {
+ /*
+ * If the host struct is smaller than the target struct, or
+ * requires less alignment and thus packs into less space,
+ * then the host can return more entries than we can pass
+ * on to the guest.
+ */
+ if (toff == 0) {
+ toff = -TARGET_EINVAL; /* result buffer is too small */
break;
}
- de->d_reclen = tswap16(reclen);
- tswap64s((uint64_t *)&de->d_ino);
- tswap64s((uint64_t *)&de->d_off);
- de = (struct linux_dirent64 *)((char *)de + reclen);
- len -= reclen;
+ /*
+ * Return what we have, resetting the file pointer to the
+ * location of the first record not returned.
+ */
+ lseek64(dirfd, prev_diroff, SEEK_SET);
+ break;
}
+
+ prev_diroff = hde->d_off;
+ tde->d_ino = tswap64(hde->d_ino);
+ tde->d_off = tswap64(hde->d_off);
+ tde->d_reclen = tswap16(treclen);
+ tde->d_type = hde->d_type;
+ memcpy(tde->d_name, hde->d_name, namelen);
}
- unlock_user(dirp, arg2, ret);
- return ret;
+
+ unlock_user(tdirp, arg2, toff);
+ return toff;
}
#endif /* TARGET_NR_getdents64 */
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PULL 5/5] linux-user: fix Coverity CID 1464101
2021-11-22 8:23 [PULL 0/5] Linux user for 6.2 patches Laurent Vivier
` (3 preceding siblings ...)
2021-11-22 8:23 ` [PULL 4/5] linux-user: Rewrite do_getdents, do_getdents64 Laurent Vivier
@ 2021-11-22 8:23 ` Laurent Vivier
2021-11-22 11:13 ` [PULL 0/5] Linux user for 6.2 patches Richard Henderson
5 siblings, 0 replies; 7+ messages in thread
From: Laurent Vivier @ 2021-11-22 8:23 UTC (permalink / raw)
To: qemu-devel; +Cc: Peter Maydell, richard.henderson, Laurent Vivier
target_mmap() can fail and return -1, but we don't check for that and
instead assume it's always valid.
Fixes: db2af69d6ba8 ("linux-user: Add infrastructure for a signal trampoline page")
Cc: richard.henderson@linaro.org
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20211121151711.331653-1-laurent@vivier.eu>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
linux-user/elfload.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 5da8c02d0822..767f54c76dc5 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -3254,9 +3254,13 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
* Otherwise, allocate a private page to hold them.
*/
if (TARGET_ARCH_HAS_SIGTRAMP_PAGE) {
- abi_ulong tramp_page = target_mmap(0, TARGET_PAGE_SIZE,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
+ abi_long tramp_page = target_mmap(0, TARGET_PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (tramp_page == -1) {
+ return -errno;
+ }
+
setup_sigtramp(tramp_page);
target_mprotect(tramp_page, TARGET_PAGE_SIZE, PROT_READ | PROT_EXEC);
}
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PULL 0/5] Linux user for 6.2 patches
2021-11-22 8:23 [PULL 0/5] Linux user for 6.2 patches Laurent Vivier
` (4 preceding siblings ...)
2021-11-22 8:23 ` [PULL 5/5] linux-user: fix Coverity CID 1464101 Laurent Vivier
@ 2021-11-22 11:13 ` Richard Henderson
5 siblings, 0 replies; 7+ messages in thread
From: Richard Henderson @ 2021-11-22 11:13 UTC (permalink / raw)
To: Laurent Vivier, qemu-devel
On 11/22/21 9:23 AM, Laurent Vivier wrote:
> The following changes since commit 8627edfb3f1fca24a96a0954148885c3241c10f8:
>
> Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging (2021-11-19 17:16:57 +0100)
>
> are available in the Git repository at:
>
> git://github.com/vivier/qemu.git tags/linux-user-for-6.2-pull-request
>
> for you to fetch changes up to 802ae45e94151a6d3ee20eadcb865cf6c875df34:
>
> linux-user: fix Coverity CID 1464101 (2021-11-22 09:17:08 +0100)
>
> ----------------------------------------------------------------
> linux-user pull request 20211122
>
> Fixes for CID 1464101 and gilab #704
>
> ----------------------------------------------------------------
>
> Laurent Vivier (1):
> linux-user: fix Coverity CID 1464101
>
> Richard Henderson (4):
> linux-user: Split out do_getdents, do_getdents64
> linux-user: Always use flexible arrays for dirent d_name
> linux-user: Fix member types of target_dirent64
> linux-user: Rewrite do_getdents, do_getdents64
>
> linux-user/elfload.c | 10 +-
> linux-user/syscall.c | 314 +++++++++++++++++++-------------------
> linux-user/syscall_defs.h | 12 +-
> 3 files changed, 172 insertions(+), 164 deletions(-)
Applied, thanks.
r~
^ permalink raw reply [flat|nested] 7+ messages in thread