tlb_finish_mmu(madv_behavior->tlb);
}
-static bool is_valid_madvise(unsigned long start, size_t len_in, int behavior)
+/**
+ * check_input_range() - Check if the requested range is valid.
+ * @start: Start address of madvise-requested address range.
+ * @len_in: Length of madvise-requested address range.
+ *
+ * Returns: 0 if the input range is valid, otherwise an error code.
+ */
+static int check_input_range(unsigned long start, size_t len_in)
{
size_t len;
- if (!madvise_behavior_valid(behavior))
- return false;
-
if (!PAGE_ALIGNED(start))
- return false;
+ return -EINVAL;
len = PAGE_ALIGN(len_in);
/* Check to see whether len was rounded up from small -ve to zero */
if (len_in && !len)
- return false;
+ return -EINVAL;
if (start + len < start)
- return false;
-
- return true;
-}
+ return -EINVAL;
-/*
- * madvise_should_skip() - Return if the request is invalid or nothing.
- * @start: Start address of madvise-requested address range.
- * @len_in: Length of madvise-requested address range.
- * @behavior: Requested madvise behavior.
- * @err: Pointer to store an error code from the check.
- *
- * If the specified behaviour is invalid or nothing would occur, we skip the
- * operation. This function returns true in the cases, otherwise false. In
- * the former case we store an error on @err.
- */
-static bool madvise_should_skip(unsigned long start, size_t len_in,
- int behavior, int *err)
-{
- if (!is_valid_madvise(start, len_in, behavior)) {
- *err = -EINVAL;
- return true;
- }
- if (start + PAGE_ALIGN(len_in) == start) {
- *err = 0;
- return true;
- }
- return false;
+ return 0;
}
static bool is_madvise_populate(struct madvise_behavior *madv_behavior)
.tlb = &tlb,
};
- if (madvise_should_skip(start, len_in, behavior, &error))
+ if (!madvise_behavior_valid(behavior))
+ return -EINVAL;
+
+ error = check_input_range(start, len_in);
+ if (error || !len_in)
return error;
+
error = madvise_lock(&madv_behavior);
if (error)
return error;
size_t len_in = iter_iov_len(iter);
int error;
- if (madvise_should_skip(start, len_in, behavior, &error))
+ error = check_input_range(start, len_in);
+ if (error || !len_in)
ret = error;
else
ret = madvise_do_behavior(start, len_in, &madv_behavior);
goto release_task;
}
+ if (!madvise_behavior_valid(behavior)) {
+ ret = -EINVAL;
+ goto release_mm;
+ }
+
/*
* We need only perform this check if we are attempting to manipulate a
* remote process's address space.
ASSERT_EQ(munmap(map, pagesize), 0);
}
+/*
+ * Test that invalid advice is rejected even when the iovec has zero total
+ * length. A request with valid advice and zero length is a noop, but
+ * invalid advice should still fail with EINVAL.
+ */
+TEST_F(process_madvise, invalid_advice_zero_length)
+{
+ struct iovec vec = {
+ .iov_base = NULL,
+ .iov_len = 0,
+ };
+ int pidfd = self->pidfd;
+ ssize_t ret;
+
+ errno = 0;
+ ret = sys_process_madvise(pidfd, &vec, 1, -1, 0);
+ ASSERT_EQ(ret, -1);
+ ASSERT_EQ(errno, EINVAL);
+
+ errno = 0;
+ ret = sys_process_madvise(pidfd, &vec, 1, MADV_DONTNEED, 0);
+ ASSERT_EQ(ret, 0);
+
+ ret = sys_process_madvise(pidfd, NULL, 0, -1, 0);
+ ASSERT_EQ(ret, -1);
+ ASSERT_EQ(errno, EINVAL);
+}
+
/*
* Test process_madvise() with an invalid flag value. Currently, only a flag
* value of 0 is supported. This test is reserved for the future, e.g., if