Skip to contentSkip to conversation

vboxsf: fix old signature detection

PATCH In review Arnd Bergmann 6 participants 11 messages Sep 27, 2021 – May 22, 2022

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 vboxsf: fix old signature detection

From: Arnd Bergmann <[email protected]>

The constant-out-of-range check in clang found an actual bug in vboxsf, which leads to the detection of old mount signatures always failing:

fs/vboxsf/super.c:394:21: error: result of comparison of constant -3 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]

                       options[3] == VBSF_MOUNT_SIGNATURE_BYTE_3) {
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/vboxsf/super.c:393:21: error: result of comparison of constant -2 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]
                       options[2] == VBSF_MOUNT_SIGNATURE_BYTE_2 &&
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/vboxsf/super.c:392:21: error: result of comparison of constant -1 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]
                       options[1] == VBSF_MOUNT_SIGNATURE_BYTE_1 &&
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~

The problem is that the pointer is of type 'unsigned char' but the constant is a 'char'. My first idea was to change the type of the pointer to 'char *', but I noticed that this was the original code and it got changed after 'smatch' complained about this.

I don't know if there is a bug in smatch here, but it sounds to me that clang's warning is correct. Forcing the constants to an unsigned type should make the code behave consistently and avoid the warning on both.

Fixes 9d682ea6bcc7 ("vboxsf: Fix the check for the old binary mount-arguments struct")
Cc Dan Carpenter <[email protected]>
---
 fs/vboxsf/super.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
fs/vboxsf/super.c +4 -4
index 4f5e59f06284..84e2236021de 100644
@@ -21,10 +21,10 @@
 
 #define VBOXSF_SUPER_MAGIC 0x786f4256 /* 'VBox' little endian */
 
-#define VBSF_MOUNT_SIGNATURE_BYTE_0 ('\000')
-#define VBSF_MOUNT_SIGNATURE_BYTE_1 ('\377')
-#define VBSF_MOUNT_SIGNATURE_BYTE_2 ('\376')
-#define VBSF_MOUNT_SIGNATURE_BYTE_3 ('\375')
+#define VBSF_MOUNT_SIGNATURE_BYTE_0 (u8)('\000')
+#define VBSF_MOUNT_SIGNATURE_BYTE_1 (u8)('\377')
+#define VBSF_MOUNT_SIGNATURE_BYTE_2 (u8)('\376')
+#define VBSF_MOUNT_SIGNATURE_BYTE_3 (u8)('\375')
 
 static int follow_symlinks;
 module_param(follow_symlinks, int, 0444);
Signature · 1 line
2.29.2
Reviewed-by on v1 1/1
#1 lore raw

Hi,

Quoted 9 lines · Arnd Bergmann

From: Arnd Bergmann <[email protected]>

The constant-out-of-range check in clang found an actual bug in vboxsf, which leads to the detection of old mount signatures always failing:

fs/vboxsf/super.c:394:21: error: result of comparison of constant -3 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]

                       options[3] == VBSF_MOUNT_SIGNATURE_BYTE_3) {
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~

This actually seems to be a clang bug though, or at least a weird interpretation (and different from gcc) of the C spec.

VBSF_MOUNT_SIGNATURE_BYTE_3 is defined as:

#define VBSF_MOUNT_SIGNATURE_BYTE_3 ('\375')

The C-spec:

http://port70.net/~nsz/c/c11/n1570.html#6.4.4.4p5

Says the following:

"The octal digits that follow the backslash in an octal escape sequence are taken to be part of the construction of a single character for an integer character constant or of a single wide character for a wide character constant. The numerical value of the octal integer so formed specifies the value of the desired character or wide character."

Character constants have a type of int, so 0375
clearly fits in the range of that.

I guess the problem is that gcc sees this as

const int VBSF_MOUNT_SIGNATURE_BYTE_3 = 0375;

Where as clang sees this as:

const int VBSF_MOUNT_SIGNATURE_BYTE_3 = (char)0375;

Which is a nice subtle incompatibility between the 2 :|

With that said, the patch is fine and I have no objections against it:

Although maybe it is better to actually remove any ambiguity and just replace the defines with:

static const u8 VBSF_MOUNT_SIGNATURE_BYTE_0 = 0000; static const u8 VBSF_MOUNT_SIGNATURE_BYTE_1 = 0377; static const u8 VBSF_MOUNT_SIGNATURE_BYTE_2 = 0376; static const u8 VBSF_MOUNT_SIGNATURE_BYTE_3 = 0375;

?

Regards,

Hans

Quoted 44 lines

fs/vboxsf/super.c:393:21: error: result of comparison of constant -2 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]

                       options[2] == VBSF_MOUNT_SIGNATURE_BYTE_2 &&
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/vboxsf/super.c:392:21: error: result of comparison of constant -1 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]
                       options[1] == VBSF_MOUNT_SIGNATURE_BYTE_1 &&
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~

The problem is that the pointer is of type 'unsigned char' but the constant is a 'char'. My first idea was to change the type of the pointer to 'char *', but I noticed that this was the original code and it got changed after 'smatch' complained about this.

I don't know if there is a bug in smatch here, but it sounds to me that clang's warning is correct. Forcing the constants to an unsigned type should make the code behave consistently and avoid the warning on both.

Fixes 9d682ea6bcc7 ("vboxsf: Fix the check for the old binary mount-arguments struct")
Cc Dan Carpenter <[email protected]>
---
 fs/vboxsf/super.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
fs/vboxsf/super.c +4 -4
index 4f5e59f06284..84e2236021de 100644
@@ -21,10 +21,10 @@
 
 #define VBOXSF_SUPER_MAGIC 0x786f4256 /* 'VBox' little endian */
 
-#define VBSF_MOUNT_SIGNATURE_BYTE_0 ('\000')
-#define VBSF_MOUNT_SIGNATURE_BYTE_1 ('\377')
-#define VBSF_MOUNT_SIGNATURE_BYTE_2 ('\376')
-#define VBSF_MOUNT_SIGNATURE_BYTE_3 ('\375')
+#define VBSF_MOUNT_SIGNATURE_BYTE_0 (u8)('\000')
+#define VBSF_MOUNT_SIGNATURE_BYTE_1 (u8)('\377')
+#define VBSF_MOUNT_SIGNATURE_BYTE_2 (u8)('\376')
+#define VBSF_MOUNT_SIGNATURE_BYTE_3 (u8)('\375')
 
 static int follow_symlinks;
 module_param(follow_symlinks, int, 0444);
#2 lore raw

GCC handles it the same way as Clang. '\377' is -1 but in Sparse it's
255. I've added the Sparse mailing list to the CC.

regards,
dan carpenter

Quoted 114 lines · Hans de Goede

Hi,

Quoted 9 lines · Arnd Bergmann

From: Arnd Bergmann <[email protected]>

The constant-out-of-range check in clang found an actual bug in vboxsf, which leads to the detection of old mount signatures always failing:

fs/vboxsf/super.c:394:21: error: result of comparison of constant -3 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]

                       options[3] == VBSF_MOUNT_SIGNATURE_BYTE_3) {
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~

This actually seems to be a clang bug though, or at least a weird interpretation (and different from gcc) of the C spec.

VBSF_MOUNT_SIGNATURE_BYTE_3 is defined as:

#define VBSF_MOUNT_SIGNATURE_BYTE_3 ('\375')

The C-spec:

http://port70.net/~nsz/c/c11/n1570.html#6.4.4.4p5

Says the following:

"The octal digits that follow the backslash in an octal escape sequence are taken to be part of the construction of a single character for an integer character constant or of a single wide character for a wide character constant. The numerical value of the octal integer so formed specifies the value of the desired character or wide character."

Character constants have a type of int, so 0375
clearly fits in the range of that.

I guess the problem is that gcc sees this as

const int VBSF_MOUNT_SIGNATURE_BYTE_3 = 0375;

Where as clang sees this as:

const int VBSF_MOUNT_SIGNATURE_BYTE_3 = (char)0375;

Which is a nice subtle incompatibility between the 2 :|

With that said, the patch is fine and I have no objections against it:

Although maybe it is better to actually remove any ambiguity and just replace the defines with:

static const u8 VBSF_MOUNT_SIGNATURE_BYTE_0 = 0000; static const u8 VBSF_MOUNT_SIGNATURE_BYTE_1 = 0377; static const u8 VBSF_MOUNT_SIGNATURE_BYTE_2 = 0376; static const u8 VBSF_MOUNT_SIGNATURE_BYTE_3 = 0375;

?

Regards,

Hans

Quoted 44 lines

fs/vboxsf/super.c:393:21: error: result of comparison of constant -2 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]

                       options[2] == VBSF_MOUNT_SIGNATURE_BYTE_2 &&
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
fs/vboxsf/super.c:392:21: error: result of comparison of constant -1 with expression of type 'unsigned char' is always false [-Werror,-Wtautological-constant-out-of-range-compare]
                       options[1] == VBSF_MOUNT_SIGNATURE_BYTE_1 &&
                       ~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~

The problem is that the pointer is of type 'unsigned char' but the constant is a 'char'. My first idea was to change the type of the pointer to 'char *', but I noticed that this was the original code and it got changed after 'smatch' complained about this.

I don't know if there is a bug in smatch here, but it sounds to me that clang's warning is correct. Forcing the constants to an unsigned type should make the code behave consistently and avoid the warning on both.

Fixes 9d682ea6bcc7 ("vboxsf: Fix the check for the old binary mount-arguments struct")
Cc Dan Carpenter <[email protected]>
---
 fs/vboxsf/super.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
fs/vboxsf/super.c +4 -4
index 4f5e59f06284..84e2236021de 100644
@@ -21,10 +21,10 @@
 
 #define VBOXSF_SUPER_MAGIC 0x786f4256 /* 'VBox' little endian */
 
-#define VBSF_MOUNT_SIGNATURE_BYTE_0 ('\000')
-#define VBSF_MOUNT_SIGNATURE_BYTE_1 ('\377')
-#define VBSF_MOUNT_SIGNATURE_BYTE_2 ('\376')
-#define VBSF_MOUNT_SIGNATURE_BYTE_3 ('\375')
+#define VBSF_MOUNT_SIGNATURE_BYTE_0 (u8)('\000')
+#define VBSF_MOUNT_SIGNATURE_BYTE_1 (u8)('\377')
+#define VBSF_MOUNT_SIGNATURE_BYTE_2 (u8)('\376')
+#define VBSF_MOUNT_SIGNATURE_BYTE_3 (u8)('\375')
 
 static int follow_symlinks;
 module_param(follow_symlinks, int, 0444);
#3 lore raw
Quoted 3 lines · Dan Carpenter

GCC handles it the same way as Clang. '\377' is -1 but in Sparse it's
255. I've added the Sparse mailing list to the CC.

More specifically, ' think '\377' may be either -1 or 255 depending on the architecture.
On most architectures, 'char' is implicitly signed, but on some others it is not.

The original code before 9d682ea6bcc7 should have worked either way because both sides of the comparison were the same 'char' type, marking one of them as explicitly 'unsigned char' seems to have broken all architectures on which the type is implicitly 'signed'.

Arnd

#4 lore raw
Quoted 5 lines · Arnd Bergmann

More specifically, ' think '\377' may be either -1 or 255 depending on the architecture.
On most architectures, 'char' is implicitly signed, but on some others it is not.

Yeah. That code is just broken.

And Arnd, your patch may be "conceptually minimal", in that it keeps thed broken code and makes it work. But it just dials up the oddity to 11.

The proper patch is just this appended thing that stops playing silly games, and just uses "memcmp()".

I've verified that with sane build configurations, it just generates

        testq   %rsi, %rsi
        je      .L25
        cmpl    $-33620224, (%rsi)
        je      .L31

for that

        if (data && !memcmp(data, VBSF_MOUNT_SIGNATURE, 4)) {

test. With a lot of crazy debug options you'll actually see the "memcmp()", but the bad code generation is the least of your options in that case.

Linus

 fs/vboxsf/super.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)
fs/vboxsf/super.c +2 -10
index 4f5e59f06284..37dd3fe5b1e9 100644
@@ -21,10 +21,7 @@
 
 #define VBOXSF_SUPER_MAGIC 0x786f4256 /* 'VBox' little endian */
 
-#define VBSF_MOUNT_SIGNATURE_BYTE_0 ('\000')
-#define VBSF_MOUNT_SIGNATURE_BYTE_1 ('\377')
-#define VBSF_MOUNT_SIGNATURE_BYTE_2 ('\376')
-#define VBSF_MOUNT_SIGNATURE_BYTE_3 ('\375')
+static const unsigned char VBSF_MOUNT_SIGNATURE[4] = "\000\377\376\375";
 
 static int follow_symlinks;
 module_param(follow_symlinks, int, 0444);
@@ -386,12 +383,7 @@ static int vboxsf_setup(void)
 
 static int vboxsf_parse_monolithic(struct fs_context *fc, void *data)
 {
-	unsigned char *options = data;
-
-	if (options && options[0] == VBSF_MOUNT_SIGNATURE_BYTE_0 &&
-		       options[1] == VBSF_MOUNT_SIGNATURE_BYTE_1 &&
-		       options[2] == VBSF_MOUNT_SIGNATURE_BYTE_2 &&
-		       options[3] == VBSF_MOUNT_SIGNATURE_BYTE_3) {
+	if (data && !memcmp(data, VBSF_MOUNT_SIGNATURE, 4)) {
 		vbg_err("vboxsf: Old binary mount data not supported, remove obsolete mount.vboxsf and/or update your VBoxService.\n");
 		return -EINVAL;
 	}
#5 lore raw

Hi Linus,

Quoted 23 lines · Linus Torvalds
Quoted 5 lines · Arnd Bergmann

More specifically, ' think '\377' may be either -1 or 255 depending on the architecture.
On most architectures, 'char' is implicitly signed, but on some others it is not.

Yeah. That code is just broken.

And Arnd, your patch may be "conceptually minimal", in that it keeps thed broken code and makes it work. But it just dials up the oddity to 11.

The proper patch is just this appended thing that stops playing silly games, and just uses "memcmp()".

I've verified that with sane build configurations, it just generates

        testq   %rsi, %rsi
        je      .L25
        cmpl    $-33620224, (%rsi)
        je      .L31
Quoted 7 lines

for that

        if (data && !memcmp(data, VBSF_MOUNT_SIGNATURE, 4)) {

test. With a lot of crazy debug options you'll actually see the "memcmp()", but the bad code generation is the least of your options in that case.

I agree that your suggestion is to be the best solution, so how do we move forward with this, do I turn this into a proper patch with you as the author and Arnd as Reported-by and if yes may I add your Signed-off-by to the patch ?

Or do I make myself author and set you as Suggested-by ?

Regards,

Hans

#6 lore raw
Quoted 8 lines · Hans de Goede
Quoted 7 lines · Linus Torvalds
Quoted 5 lines · Arnd Bergmann

More specifically, ' think '\377' may be either -1 or 255 depending on the architecture.
On most architectures, 'char' is implicitly signed, but on some others it is not.

Quoted 5 lines
Quoted 5 lines

Yeah. That code is just broken.

And Arnd, your patch may be "conceptually minimal", in that it keeps thed broken code and makes it work. But it just dials up the oddity to 11.

Thank you for addressing it. I usually try to avoid overthinking changes to "unusual" code like this, but your solution is clearly an improvement.

What really threw me off this time is that my first attempt to address the warning was an exact revert of 9d682ea6bcc7 ("vboxsf: Fix the check for the old binary mount-arguments struct"), which in turn came from a tool that is usually correct and and that both Dan and Al thought the original patch was correct when it looked like it turned a working (though unusual) implementation into a broken one.

Quoted 4 lines

I agree that your suggestion is to be the best solution, so how do we move forward with this, do I turn this into a proper patch with you as the author and Arnd as Reported-by and if yes may I add your Signed-off-by to the patch ?

It's already upstream, see d5f6545934c4 ("qnx4: work around gcc false positive warning bug").

Arnd

#7 lore raw

Hi,

Quoted 26 lines · Arnd Bergmann
Quoted 8 lines · Hans de Goede
Quoted 7 lines · Linus Torvalds
Quoted 5 lines · Arnd Bergmann

More specifically, ' think '\377' may be either -1 or 255 depending on the architecture.
On most architectures, 'char' is implicitly signed, but on some others it is not.

Quoted 5 lines
Quoted 5 lines

Yeah. That code is just broken.

And Arnd, your patch may be "conceptually minimal", in that it keeps thed broken code and makes it work. But it just dials up the oddity to 11.

Thank you for addressing it. I usually try to avoid overthinking changes to "unusual" code like this, but your solution is clearly an improvement.

What really threw me off this time is that my first attempt to address the warning was an exact revert of 9d682ea6bcc7 ("vboxsf: Fix the check for the old binary mount-arguments struct"), which in turn came from a tool that is usually correct and and that both Dan and Al thought the original patch was correct when it looked like it turned a working (though unusual) implementation into a broken one.

Quoted 7 lines
Quoted 4 lines

I agree that your suggestion is to be the best solution, so how do we move forward with this, do I turn this into a proper patch with you as the author and Arnd as Reported-by and if yes may I add your Signed-off-by to the patch ?

It's already upstream, see d5f6545934c4 ("qnx4: work around gcc false positive warning bug").

Ah, actually you mean: 9b3b353ef330 ("vboxfs: fix broken legacy mount signature checking"), but other then that yes you're right it is already upstream.

Thank you Arnd and thank you Linus.

Regards,

Hans

#8 lore raw
Quoted 6 lines · Hans de Goede
Quoted 2 lines · Arnd Bergmann

It's already upstream, see d5f6545934c4 ("qnx4: work around gcc false positive warning bug").

Ah, actually you mean: 9b3b353ef330 ("vboxfs: fix broken legacy mount signature checking"),

Yes, that's the right one of course.

Arnd

#9 lore raw
In reply to Dan Carpenter
Quoted 2 lines · Dan Carpenter

GCC handles it the same way as Clang. '\377' is -1 but in Sparse it's
255. I've added the Sparse mailing list to the CC.

FTR, while examples are not normative, this:

EXAMPLE 2 Consider implementations that use two's complement representation for integers and eight bits for objects that have type char. In an implementation in which type char has the same range of values as signed char, the integer character constant '\xFF' has the value -1; if type char has the same range of values as unsigned char, the character constant '\xFF' has the value +255.

doesn't leave any ambiguity or (implementation|un)-definednes, and sparse interpreting '\377' as 255 independent of its target->unsigned_char is a plain bug in sparse.

Rasmus

#10 lore raw

PATCH 1/1 fix zero/sign extension of integer character constants

From: Luc Van Oostenryck <[email protected]>

An integer character constant has type 'int' but, subtly enough, its value is the one of a 'char' converted to an 'int'.

So, do this conversion.
Also set the type of wide character constants from 'long' to 'wchar_t'.

---
 expression.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
expression.c +8 -1
index 221d7780a76e..e3b58cb5b653 100644
@@ -427,8 +427,15 @@ struct token *primary_expression(struct token *token, struct expression **tree)
 	case TOKEN_CHAR ... TOKEN_WIDE_CHAR_EMBEDDED_3:
 		expr = alloc_expression(token->pos, EXPR_VALUE);
 		expr->flags = CEF_SET_CHAR;
-		expr->ctype = token_type(token) < TOKEN_WIDE_CHAR ? &int_ctype : &long_ctype;
 		get_char_constant(token, &expr->value);
+
+		// TODO: handle 'u8', 'u' & 'U' prefixes.
+		if (token_type(token) < TOKEN_WIDE_CHAR) {
+			expr->ctype = &int_ctype;
+			expr->value = extend_value(expr->value, &char_ctype);
+		} else {
+			expr->ctype = wchar_ctype;
+		}
 		token = token->next;
 		break;
 
Signature · 1 line
2.36.1