regression 1014: test out of bounds SDP references

Invoke the SDP test TA with output SDP memory reference outside the
allocated SDP buffers. Such invocation with invalid references should
not reach the trusted application and be nicely rejected from either
TEE client library, TEE Linux driver or the TEE core itself.

Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
Acked-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/host/xtest/regression_1000.c b/host/xtest/regression_1000.c
index 634c80e..12cca59 100644
--- a/host/xtest/regression_1000.c
+++ b/host/xtest/regression_1000.c
@@ -1257,6 +1257,11 @@
 	ret = sdp_basic_test(test, size, loop, ion_heap, rnd_offset, 0);
 	ADBG_EXPECT(c, 1, ret);
 	Do_ADBG_EndSubCase(c, "SDP: NSec CA invokes SDP pTA (should fail)");
+
+	Do_ADBG_BeginSubCase(c, "SDP: Invoke TA with out of bounds SDP memref");
+	ret = sdp_out_of_bounds_memref_test(size, ion_heap, 0);
+	ADBG_EXPECT(c, 0, ret);
+	Do_ADBG_EndSubCase(c, NULL);
 }
 ADBG_CASE_DEFINE(regression, 1014, xtest_tee_test_1014,
 		"Test secure data path against SDP TAs and pTAs");
diff --git a/host/xtest/sdp_basic.c b/host/xtest/sdp_basic.c
index 79d2c7c..71fd018 100644
--- a/host/xtest/sdp_basic.c
+++ b/host/xtest/sdp_basic.c
@@ -539,6 +539,133 @@
 	return err;
 }
 
+static int invoke_out_of_bounds(struct tee_ctx *ctx,
+				TEEC_SharedMemory *in, TEEC_SharedMemory *out,
+				size_t offset, size_t size,
+				bool valid_ref, int verbosity)
+{
+	TEEC_Result teerc = TEEC_ERROR_GENERIC;
+	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
+	uint32_t orig = 0;
+
+	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INPUT,
+					 TEEC_MEMREF_PARTIAL_OUTPUT,
+					 TEEC_NONE, TEEC_NONE);
+
+	op.params[0].memref.parent = in;
+	op.params[0].memref.offset = 0;
+	op.params[0].memref.size = size;
+
+	op.params[1].memref.parent = out;
+	op.params[1].memref.offset = offset;
+	op.params[1].memref.size = size;
+
+	teerc = TEEC_InvokeCommand(&ctx->sess, TA_SDP_BASIC_CMD_INJECT,
+				   &op, &orig);
+
+	/*
+	 * Invocation with invalid references should be nicely rejected by
+	 * the communication layer.
+	 * Invocation with valid references should reach the TA, whatever
+	 * result is.
+	 */
+	if ((valid_ref && orig != TEEC_ORIGIN_TRUSTED_APP) ||
+	    (!valid_ref && orig != TEEC_ORIGIN_COMMS &&
+	     (teerc != TEEC_ERROR_GENERIC ||
+	      teerc != TEEC_ERROR_BAD_PARAMETERS)))
+		goto error;
+
+	verbose("Out of bounds memref test successful:\n");
+	verbose("Shm size 0x%zx, offset 0x%zx/size 0x%zx: %s/0x%x from %s\n",
+		out->size, offset, size,
+		Do_ADBG_GetEnumName(teerc, ADBG_EnumTable_TEEC_Result), teerc,
+		Do_ADBG_GetEnumName(orig, ADBG_EnumTable_TEEC_ErrorOrigin));
+	return 0;
+
+error:
+	fprintf(stderr, "Out of bounds memref test FAILURE:\n");
+	fprintf(stderr,
+		"Shm size 0x%zx, offset 0x%zx/size 0x%zx: %s/0x%x from %s\n",
+		out->size, offset, size,
+		Do_ADBG_GetEnumName(teerc, ADBG_EnumTable_TEEC_Result),	teerc,
+		Do_ADBG_GetEnumName(orig, ADBG_EnumTable_TEEC_ErrorOrigin));
+	return 1;
+}
+
+int sdp_out_of_bounds_memref_test(size_t size, int ion_heap, int verbosity)
+{
+	struct tee_ctx ctx = { };
+	int err = 0;
+	int fd = -1;
+	TEEC_Result teerc = TEEC_ERROR_GENERIC;
+	TEEC_SharedMemory in = { };
+	TEEC_SharedMemory *out = NULL;
+
+	if (create_tee_ctx(&ctx, TEST_NS_TO_TA))
+		return -1;
+
+	fd = allocate_ion_buffer(size, ion_heap, verbosity);
+	if (fd < 0) {
+		verbose("SDP alloc failed (%zu bytes) in ION heap %d: %d\n",
+			size, ion_heap, fd);
+		err = 1;
+		goto bail;
+	}
+	if (tee_register_buffer(&ctx, (void **)&out, fd)) {
+		err = 1;
+		goto bail;
+	}
+
+	/*
+	 * The ION driver will decide how much SDP memory is being allocated.
+	 * Rely on this size to test out of bounds reference cases.
+	 */
+	size = out->size;
+
+	in.size = size;
+	in.flags = TEEC_MEM_INPUT;
+	teerc = TEEC_AllocateSharedMemory(&ctx.ctx, &in);
+	if (teerc) {
+		verbose("failed to allocate memory\n");
+		goto bail;
+	}
+
+	if (verbosity) {
+		/* Valid case: reference inside allocated buffer: last byte */
+		err += invoke_out_of_bounds(&ctx, &in, out, size - 1, 1,
+					    true, verbosity);
+	}
+
+	/* Reference overflows allocated buffer by 1 byte */
+	err += invoke_out_of_bounds(&ctx, &in, out, size - 1, 2,
+				    false, verbosity);
+
+	/* Reference oveflows allocated buffer by more than 4kB byte */
+	err += invoke_out_of_bounds(&ctx, &in, out, size - 1, 5000,
+				    false, verbosity);
+
+	/* Offset exceeds allocated buffer size value by 1 byte */
+	err += invoke_out_of_bounds(&ctx, &in, out, size, 1,
+				    false, verbosity);
+
+	/* Offset exceeds allocated size value by 4kByte */
+	err += invoke_out_of_bounds(&ctx, &in, out, size, 4096,
+				    false, verbosity);
+
+	/* Offset + size overflows offset value */
+	err += invoke_out_of_bounds(&ctx, &in, out, 2, ~0,
+				    false, verbosity);
+
+	TEEC_ReleaseSharedMemory(&in);
+bail:
+	tee_deregister_buffer(&ctx, out);
+	if (fd >= 0)
+		close(fd);
+	finalize_tee_ctx(&ctx);
+
+	return err;
+}
+
 #define _TO_STR(x) #x
 #define TO_STR(x) _TO_STR(x)
 
@@ -640,5 +767,10 @@
 			     rnd_offset, verbosity);
 	CHECK_RESULT(err, 1, return 1);
 
+	verbose("\nSecure Data Path basic access: "
+		"Invoke TA with out of bounds buffer references\n");
+	err = sdp_out_of_bounds_memref_test(test_size, ion_heap, verbosity);
+	CHECK_RESULT(err, 0, return 1);
+
 	return 0;
 }
diff --git a/host/xtest/sdp_basic.h b/host/xtest/sdp_basic.h
index 001dd69..c329262 100644
--- a/host/xtest/sdp_basic.h
+++ b/host/xtest/sdp_basic.h
@@ -45,4 +45,6 @@
 			  size_t size, size_t loop, int ion_heap,
 			  int rnd_offset, int verbosity);
 
+int sdp_out_of_bounds_memref_test(size_t size, int ion_heap, int verbosity);
+
 #endif /* XTEST_SDP_BASIC_H */