diff options
-rw-r--r-- | clang-plugin/gvariant-checker.cpp | 75 | ||||
-rw-r--r-- | tests/gvariant-get.c | 40 |
2 files changed, 113 insertions, 2 deletions
diff --git a/clang-plugin/gvariant-checker.cpp b/clang-plugin/gvariant-checker.cpp index a39bf59..eb09630 100644 --- a/clang-plugin/gvariant-checker.cpp +++ b/clang-plugin/gvariant-checker.cpp @@ -212,6 +212,63 @@ _compare_types (const QualType actual_type, const QualType expected_type, flags, context); } +/* + * Return true if the given @type is known to differ in width on different + * operating systems or processor architectures. This is important for + * portability, as otherwise the static analysis is only testing correctness + * for the current platform. + * + * For example, + * g_variant_get (x, "i", &some_long) + * is valid on 32-bit machines (where long is 32 bits wide), but invalid on + * 64-bit machines (where it is 64 bits wide). We want to flag the problem + * regardless of whether the analyser is run on a 32- or 64-bit host. + */ +static bool +_type_is_arch_dependent (const QualType type, ASTContext &context) +{ + /* Strip off any pointers. */ + const PointerType *pointer_type = dyn_cast<PointerType> (type); + + if (pointer_type != NULL) { + return _type_is_arch_dependent (pointer_type->getPointeeType (), + context); + } + + /* If it’s a typedef type, assume it’s not architecture dependent. + * This is a tricky one, but is required because the Clang type system + * ignores typedefs and preprocessor statements when comparing types, so + * hasSameType(gint64, long) + * returns true, just the same as + * hasSameType (long, long) + * return true. We want to avoid g* basic types (such as gint64) being + * considered as architecture-dependent, since they carefully use + * preprocessor voodoo to avoid that. + * + * So, assume that if the programmer has used an architecture-dependent + * type in a typedef, they know enough to make the typedef + * architecture-dependent. + * + * But glong is a typedef, so we have to special case that. Sigh. */ + const TypedefType *typedef_type = dyn_cast<TypedefType> (type); + + if (typedef_type != NULL) { + const std::string typedef_name = + typedef_type->getDecl ()->getNameAsString (); + return (typedef_name == "glong" || + typedef_name == "gulong"); + } + + /* Well-known architecture-dependent types. + * + * Reference: https://software.intel.com/en-us/articles/ + * size-of-long-integer-type-on-different-architecture-and-os + */ + return (context.hasSameType (type, context.LongTy) || + context.hasSameType (type, context.UnsignedLongTy) || + context.hasSameType (type, context.LongDoubleTy)); +} + /* Consume a single variadic argument from the varargs array, checking that one * exists and has the given @expected_type. * @@ -300,8 +357,22 @@ _consume_variadic_argument (QualType expected_type, return false; } else if (!is_null_constant) { /* Normal case. */ - if (!_compare_types (actual_type, expected_type, - flags, context)) { + bool type_error = !_compare_types (actual_type, expected_type, + flags, context); + bool arch_error = _type_is_arch_dependent (actual_type, + context); + + if (arch_error) { + Debug::emit_error ("Expected a GVariant variadic " + "argument of type %0 but saw one " + "of type %1. These types are not " + "compatible on every architecture.", + compiler, arg->getLocStart ()) + << expected_type + << actual_type; + + return false; + } else if (type_error) { Debug::emit_error ("Expected a GVariant variadic " "argument of type %0 but saw one " "of type %1.", compiler, diff --git a/tests/gvariant-get.c b/tests/gvariant-get.c index 0f0d965..2a8d1a2 100644 --- a/tests/gvariant-get.c +++ b/tests/gvariant-get.c @@ -510,3 +510,43 @@ va_list some_va_list; g_variant_get_va (existing_variant, "invalid", NULL, &some_va_list); } + +/* + * Expected a GVariant variadic argument of type 'gint64 *' (aka 'long *') but saw one of type 'glong *' (aka 'long *'). These types are not compatible on every architecture. + * g_variant_get (existing_variant, "x", &some_long); + * ^ + */ +{ + glong some_long; + g_variant_get (existing_variant, "x", &some_long); +} + +/* + * Expected a GVariant variadic argument of type 'gint64 *' (aka 'long *') but saw one of type 'long *'. These types are not compatible on every architecture. + * g_variant_get (existing_variant, "x", &some_long); + * ^ + */ +{ + long some_long; + g_variant_get (existing_variant, "x", &some_long); +} + +/* + * Expected a GVariant variadic argument of type 'guint64 *' (aka 'unsigned long *') but saw one of type 'gulong *' (aka 'unsigned long *'). These types are not compatible on every architecture. + * g_variant_get (existing_variant, "t", &some_long); + * ^ + */ +{ + gulong some_long; + g_variant_get (existing_variant, "t", &some_long); +} + +/* + * Expected a GVariant variadic argument of type 'gint32 *' (aka 'int *') but saw one of type 'glong *' (aka 'long *'). These types are not compatible on every architecture. + * g_variant_get (existing_variant, "i", &some_long); + * ^ + */ +{ + glong some_long; + g_variant_get (existing_variant, "i", &some_long); +} |