Fully handle size_t lengths in AIO requests.

First, update the return types of aio_return() and aio_waitcomplete() to
ssize_t.

POSIX requires aio_return() to return a ssize_t so that it can represent
all return values from read() and write().  aio_waitcomplete() should use
ssize_t for the same reason.

aio_return() has used ssize_t in <aio.h> since r31620 but the manpage and
system call entry were not updated.  aio_waitcomplete() has always
returned int.

Note that this does not require new system call stubs as this is
effectively only an API change in how the compiler interprets the return
value.

Second, allow aio_nbytes values up to IOSIZE_MAX instead of just INT_MAX.

aio_read/write should now honor the same length limits as normal read/write.

Third, use longs instead of ints in the aio_return() and aio_waitcomplete()
system call functions so that the 64-bit size_t in the in-kernel aiocb
isn't truncated to 32-bits before being copied out to userland or
being returned.

Finally, a simple test has been added to verify the bounds checking on the
maximum read size from a file.
This commit is contained in:
jhb 2016-03-21 21:37:33 +00:00
parent 9910a56768
commit 6f8f2fe586
6 changed files with 88 additions and 12 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd October 7, 2006
.Dd March 21, 2016
.Dt AIO_RETURN 2
.Os
.Sh NAME
@ -34,7 +34,7 @@
.Lb libc
.Sh SYNOPSIS
.In aio.h
.Ft int
.Ft ssize_t
.Fn aio_return "struct aiocb *iocb"
.Sh DESCRIPTION
The

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd January 19, 2000
.Dd March 21, 2016
.Dt AIO_WAITCOMPLETE 2
.Os
.Sh NAME
@ -34,7 +34,7 @@
.Lb libc
.Sh SYNOPSIS
.In aio.h
.Ft int
.Ft ssize_t
.Fn aio_waitcomplete "struct aiocb **iocbp" "struct timespec *timeout"
.Sh DESCRIPTION
The

View File

@ -554,7 +554,7 @@
312 AUE_SETRESGID STD { int setresgid(gid_t rgid, gid_t egid, \
gid_t sgid); }
313 AUE_NULL OBSOL signanosleep
314 AUE_NULL STD { int aio_return(struct aiocb *aiocbp); }
314 AUE_NULL STD { ssize_t aio_return(struct aiocb *aiocbp); }
315 AUE_NULL STD { int aio_suspend( \
struct aiocb * const * aiocbp, int nent, \
const struct timespec *timeout); }
@ -643,7 +643,7 @@
358 AUE_EXTATTR_DELETE_FILE STD { int extattr_delete_file(const char *path, \
int attrnamespace, \
const char *attrname); }
359 AUE_NULL STD { int aio_waitcomplete( \
359 AUE_NULL STD { ssize_t aio_waitcomplete( \
struct aiocb **aiocbp, \
struct timespec *timeout); }
360 AUE_GETRESUID STD { int getresuid(uid_t *ruid, uid_t *euid, \

View File

@ -743,7 +743,7 @@ aio_process_rw(struct kaiocb *job)
struct file *fp;
struct uio auio;
struct iovec aiov;
int cnt;
ssize_t cnt;
int error;
int oublock_st, oublock_end;
int inblock_st, inblock_end;
@ -1447,8 +1447,7 @@ aio_aqueue(struct thread *td, struct aiocb *ujob, struct aioliojob *lj,
return (error);
}
/* XXX: aio_nbytes is later casted to signed types. */
if (job->uaiocb.aio_nbytes > INT_MAX) {
if (job->uaiocb.aio_nbytes > IOSIZE_MAX) {
uma_zfree(aiocb_zone, job);
return (EINVAL);
}
@ -1789,7 +1788,7 @@ kern_aio_return(struct thread *td, struct aiocb *ujob, struct aiocb_ops *ops)
struct proc *p = td->td_proc;
struct kaiocb *job;
struct kaioinfo *ki;
int status, error;
long status, error;
ki = p->p_aioinfo;
if (ki == NULL)
@ -2345,7 +2344,8 @@ kern_aio_waitcomplete(struct thread *td, struct aiocb **ujobp,
struct kaioinfo *ki;
struct kaiocb *job;
struct aiocb *ujob;
int error, status, timo;
long error, status;
int timo;
ops->store_aiocb(ujobp, NULL);

View File

@ -238,7 +238,7 @@ int aio_suspend(const struct aiocb * const[], int, const struct timespec *);
int aio_mlock(struct aiocb *);
#ifdef __BSD_VISIBLE
int aio_waitcomplete(struct aiocb **, struct timespec *);
ssize_t aio_waitcomplete(struct aiocb **, struct timespec *);
#endif
int aio_fsync(int op, struct aiocb *aiocbp);

View File

@ -649,6 +649,81 @@ ATF_TC_BODY(aio_md_test, tc)
aio_md_cleanup(&arg);
}
ATF_TC_WITHOUT_HEAD(aio_large_read_test);
ATF_TC_BODY(aio_large_read_test, tc)
{
char pathname[PATH_MAX];
struct aiocb cb, *cbp;
ssize_t nread;
size_t len;
int fd;
#ifdef __LP64__
int clamped;
#endif
ATF_REQUIRE_KERNEL_MODULE("aio");
ATF_REQUIRE_UNSAFE_AIO();
#ifdef __LP64__
len = sizeof(clamped);
if (sysctlbyname("debug.iosize_max_clamp", &clamped, &len, NULL, 0) ==
-1)
atf_libc_error(errno, "Failed to read debug.iosize_max_clamp");
#endif
/* Determine the maximum supported read(2) size. */
len = SSIZE_MAX;
#ifdef __LP64__
if (clamped)
len = INT_MAX;
#endif
strcpy(pathname, PATH_TEMPLATE);
fd = mkstemp(pathname);
ATF_REQUIRE_MSG(fd != -1, "mkstemp failed: %s", strerror(errno));
unlink(pathname);
memset(&cb, 0, sizeof(cb));
cb.aio_nbytes = len;
cb.aio_fildes = fd;
cb.aio_buf = NULL;
if (aio_read(&cb) == -1)
atf_tc_fail("aio_read() of maximum read size failed: %s",
strerror(errno));
nread = aio_waitcomplete(&cbp, NULL);
if (nread == -1)
atf_tc_fail("aio_waitcomplete() failed: %s", strerror(errno));
if (nread != 0)
atf_tc_fail("aio_read() from /dev/null returned data: %zd",
nread);
memset(&cb, 0, sizeof(cb));
cb.aio_nbytes = len + 1;
cb.aio_fildes = fd;
cb.aio_buf = NULL;
if (aio_read(&cb) == -1) {
if (errno == EINVAL)
goto finished;
atf_tc_fail("aio_read() of too large read size failed: %s",
strerror(errno));
}
nread = aio_waitcomplete(&cbp, NULL);
if (nread == -1) {
if (errno == EINVAL)
goto finished;
atf_tc_fail("aio_waitcomplete() failed: %s", strerror(errno));
}
atf_tc_fail(
"aio_read() of too large read size from /dev/null returned: %zd",
nread);
finished:
close(fd);
}
ATF_TP_ADD_TCS(tp)
{
@ -658,6 +733,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, aio_pty_test);
ATF_TP_ADD_TC(tp, aio_pipe_test);
ATF_TP_ADD_TC(tp, aio_md_test);
ATF_TP_ADD_TC(tp, aio_large_read_test);
return (atf_no_error());
}