diff mbox

[for-4.10,1/2] ubsan: add clang 5.0 support

Message ID 20171017113644.31866-1-roger.pau@citrix.com (mailing list archive)
State New, archived
Headers show

Commit Message

Roger Pau Monné Oct. 17, 2017, 11:36 a.m. UTC
clang 5.0 changed the layout of the type_mismatch_data structure and
introduced __ubsan_handle_type_mismatch_v1 and
__ubsan_handle_pointer_overflow.

This commit adds support for the new structure layout, adds the
missing handlers and the new types for type_check_kinds.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
Cc: George Dunlap <George.Dunlap@eu.citrix.com>
Cc: Ian Jackson <ian.jackson@eu.citrix.com>
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Tim Deegan <tim@xen.org>
Cc: Wei Liu <wei.liu2@citrix.com>
Cc: Julien Grall <julien.grall@arm.com>
---
ubsan is an optional feature, not enabled by default and not designed
to be used by production systems. Since this change only touches ubsan
code and it's a bugfix in order for clang to work, I argue it should
be merged into 4.10.
---
 xen/common/ubsan/ubsan.c | 42 ++++++++++++++++++++++++++++++++++++++++--
 xen/common/ubsan/ubsan.h | 11 +++++++++++
 2 files changed, 51 insertions(+), 2 deletions(-)

Comments

Jan Beulich Oct. 17, 2017, 12:43 p.m. UTC | #1
>>> On 17.10.17 at 13:36, <roger.pau@citrix.com> wrote:
> clang 5.0 changed the layout of the type_mismatch_data structure and
> introduced __ubsan_handle_type_mismatch_v1 and
> __ubsan_handle_pointer_overflow.
> 
> This commit adds support for the new structure layout, adds the
> missing handlers and the new types for type_check_kinds.
> 
> Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>

Acked-by: Jan Beulich <jbeulich@suse.com>
with a remark:

> +void __ubsan_handle_pointer_overflow(struct pointer_overflow_data *data,
> +				unsigned long base, unsigned long result)
> +{
> +	unsigned long flags;
> +
> +	if (suppress_report(&data->location))
> +		return;
> +
> +	ubsan_prologue(&data->location, &flags);
> +
> +	pr_err("pointer overflow:\n");
> +
> +	if (((long)base >= 0) == ((long)result >= 0))
> +		pr_err("%s of unsigned offset to %p overflowed to %p\n",
> +			base > result ? "addition" : "subtraction",

Strictly speaking you also want to make "to" conditional upon this
being an add; for subtract it ought to be "from". Or perhaps just
say overflow and underflow?

And then - is "base > result" really a valid determination of
add/subtract (or overflow/underflow)? If the pointed to type is
wider than one byte, an addition may wrap one or more times
and still yield a value larger than the starting pointer.

Jan
Roger Pau Monné Oct. 17, 2017, 12:57 p.m. UTC | #2
On Tue, Oct 17, 2017 at 06:43:28AM -0600, Jan Beulich wrote:
> >>> On 17.10.17 at 13:36, <roger.pau@citrix.com> wrote:
> > clang 5.0 changed the layout of the type_mismatch_data structure and
> > introduced __ubsan_handle_type_mismatch_v1 and
> > __ubsan_handle_pointer_overflow.
> > 
> > This commit adds support for the new structure layout, adds the
> > missing handlers and the new types for type_check_kinds.
> > 
> > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
> 
> Acked-by: Jan Beulich <jbeulich@suse.com>
> with a remark:
> 
> > +void __ubsan_handle_pointer_overflow(struct pointer_overflow_data *data,
> > +				unsigned long base, unsigned long result)
> > +{
> > +	unsigned long flags;
> > +
> > +	if (suppress_report(&data->location))
> > +		return;
> > +
> > +	ubsan_prologue(&data->location, &flags);
> > +
> > +	pr_err("pointer overflow:\n");
> > +
> > +	if (((long)base >= 0) == ((long)result >= 0))
> > +		pr_err("%s of unsigned offset to %p overflowed to %p\n",
> > +			base > result ? "addition" : "subtraction",
> 
> Strictly speaking you also want to make "to" conditional upon this
> being an add; for subtract it ought to be "from". Or perhaps just
> say overflow and underflow?
> 
> And then - is "base > result" really a valid determination of
> add/subtract (or overflow/underflow)? If the pointed to type is
> wider than one byte, an addition may wrap one or more times
> and still yield a value larger than the starting pointer.

That code is mostly adapted from upstream llvm:

https://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc?revision=313572&view=markup

But I see your point, if the types have different widths that check
won't work properly. In any case, I don't see a better way to deal
with this.

Thanks, Roger.
Jan Beulich Oct. 17, 2017, 1:04 p.m. UTC | #3
>>> On 17.10.17 at 14:57, <roger.pau@citrix.com> wrote:
> On Tue, Oct 17, 2017 at 06:43:28AM -0600, Jan Beulich wrote:
>> >>> On 17.10.17 at 13:36, <roger.pau@citrix.com> wrote:
>> > clang 5.0 changed the layout of the type_mismatch_data structure and
>> > introduced __ubsan_handle_type_mismatch_v1 and
>> > __ubsan_handle_pointer_overflow.
>> > 
>> > This commit adds support for the new structure layout, adds the
>> > missing handlers and the new types for type_check_kinds.
>> > 
>> > Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
>> 
>> Acked-by: Jan Beulich <jbeulich@suse.com>
>> with a remark:
>> 
>> > +void __ubsan_handle_pointer_overflow(struct pointer_overflow_data *data,
>> > +				unsigned long base, unsigned long result)
>> > +{
>> > +	unsigned long flags;
>> > +
>> > +	if (suppress_report(&data->location))
>> > +		return;
>> > +
>> > +	ubsan_prologue(&data->location, &flags);
>> > +
>> > +	pr_err("pointer overflow:\n");
>> > +
>> > +	if (((long)base >= 0) == ((long)result >= 0))
>> > +		pr_err("%s of unsigned offset to %p overflowed to %p\n",
>> > +			base > result ? "addition" : "subtraction",
>> 
>> Strictly speaking you also want to make "to" conditional upon this
>> being an add; for subtract it ought to be "from". Or perhaps just
>> say overflow and underflow?
>> 
>> And then - is "base > result" really a valid determination of
>> add/subtract (or overflow/underflow)? If the pointed to type is
>> wider than one byte, an addition may wrap one or more times
>> and still yield a value larger than the starting pointer.
> 
> That code is mostly adapted from upstream llvm:
> 
> https://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handler 
> s.cc?revision=313572&view=markup
> 
> But I see your point, if the types have different widths that check
> won't work properly. In any case, I don't see a better way to deal
> with this.

Well, as said - perhaps simply use

	pr_err("pointer operation %s %p to %p\n",
	       base > result ? "underflowed" : "overflowed",

Jan
diff mbox

Patch

diff --git a/xen/common/ubsan/ubsan.c b/xen/common/ubsan/ubsan.c
index fbe568562a..febf7e2afa 100644
--- a/xen/common/ubsan/ubsan.c
+++ b/xen/common/ubsan/ubsan.c
@@ -33,7 +33,10 @@  const char *type_check_kinds[] = {
 	"member call on",
 	"constructor call on",
 	"downcast of",
-	"downcast of"
+	"downcast of",
+	"upcast of",
+	"cast to virtual base of",
+	"_Nonnull binding to",
 };
 
 #define REPORTED_BIT 31
@@ -323,7 +326,6 @@  static void handle_object_size_mismatch(struct type_mismatch_data *data,
 void __ubsan_handle_type_mismatch(struct type_mismatch_data *data,
 				unsigned long ptr)
 {
-
 	if (!ptr)
 		handle_null_ptr_deref(data);
 	else if (data->alignment && !IS_ALIGNED(ptr, data->alignment))
@@ -333,6 +335,19 @@  void __ubsan_handle_type_mismatch(struct type_mismatch_data *data,
 }
 EXPORT_SYMBOL(__ubsan_handle_type_mismatch);
 
+void __ubsan_handle_type_mismatch_v1(struct type_mismatch_data_v1 *data,
+				unsigned long ptr)
+{
+	struct type_mismatch_data d = {
+		.location = data->location,
+		.type = data->type,
+		.alignment = 1ul << data->log_alignment,
+		.type_check_kind = data->type_check_kind,
+	};
+
+	__ubsan_handle_type_mismatch(&d, ptr);
+}
+
 void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data)
 {
 	unsigned long flags;
@@ -478,3 +493,26 @@  void __ubsan_handle_load_invalid_value(struct invalid_value_data *data,
 	ubsan_epilogue(&flags);
 }
 EXPORT_SYMBOL(__ubsan_handle_load_invalid_value);
+
+void __ubsan_handle_pointer_overflow(struct pointer_overflow_data *data,
+				unsigned long base, unsigned long result)
+{
+	unsigned long flags;
+
+	if (suppress_report(&data->location))
+		return;
+
+	ubsan_prologue(&data->location, &flags);
+
+	pr_err("pointer overflow:\n");
+
+	if (((long)base >= 0) == ((long)result >= 0))
+		pr_err("%s of unsigned offset to %p overflowed to %p\n",
+			base > result ? "addition" : "subtraction",
+			(void *)base, (void *)result);
+	else
+		pr_err("pointer index expression with base %p overflowed to %p\n",
+			(void *)base, (void *)result);
+
+	ubsan_epilogue(&flags);
+}
diff --git a/xen/common/ubsan/ubsan.h b/xen/common/ubsan/ubsan.h
index b2d18d4a53..2710cd423e 100644
--- a/xen/common/ubsan/ubsan.h
+++ b/xen/common/ubsan/ubsan.h
@@ -36,6 +36,13 @@  struct type_mismatch_data {
 	unsigned char type_check_kind;
 };
 
+struct type_mismatch_data_v1 {
+	struct source_location location;
+	struct type_descriptor *type;
+	unsigned char log_alignment;
+	unsigned char type_check_kind;
+};
+
 struct nonnull_arg_data {
 	struct source_location location;
 	struct source_location attr_location;
@@ -73,6 +80,10 @@  struct invalid_value_data {
 	struct type_descriptor *type;
 };
 
+struct pointer_overflow_data {
+	struct source_location location;
+};
+
 #if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__)
 typedef __int128 s_max;
 typedef unsigned __int128 u_max;