diff mbox

[v2] sparse: treat function pointers as pointers to const data

Message ID 1410093413-3075-1-git-send-email-ard.biesheuvel@linaro.org (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Ard Biesheuvel Sept. 7, 2014, 12:36 p.m. UTC
This code snippet:

static void bar(void const *arg)
{
	int (*foo)(void) = arg;
}

produces the following warning:

test.c:4:28: warning: incorrect type in initializer (different modifiers)
test.c:4:28:    expected int ( *foo )( ... )
test.c:4:28:    got void const *arg

which is caused by the fact that the function pointer 'foo' is not annotated
as being a pointer to const data. However, dereferencing a function pointer
does not produce an lvalue, so a function pointer points to const data by
definition, and we should treat it accordingly.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---

OK, so while my v1 did solve the example case, it turns out universally treating
function pointers as pointers to const data produces so much fallout that it
does more harm than good.

Instead, this v2 only addresses function pointer initializers and assignments.

Comments

Josh Triplett Sept. 7, 2014, 5:14 p.m. UTC | #1
On Sun, Sep 07, 2014 at 02:36:53PM +0200, Ard Biesheuvel wrote:
> This code snippet:
> 
> static void bar(void const *arg)
> {
> 	int (*foo)(void) = arg;
> }
> 
> produces the following warning:
> 
> test.c:4:28: warning: incorrect type in initializer (different modifiers)
> test.c:4:28:    expected int ( *foo )( ... )
> test.c:4:28:    got void const *arg
> 
> which is caused by the fact that the function pointer 'foo' is not annotated
> as being a pointer to const data. However, dereferencing a function pointer
> does not produce an lvalue, so a function pointer points to const data by
> definition, and we should treat it accordingly.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

As a data point: gcc does not warn about this case either, whereas it
does warn about "int *foo = arg".  So, this seems fine to me.

Reviewed-by: Josh Triplett <josh@joshtriplett.org>

> OK, so while my v1 did solve the example case, it turns out universally treating
> function pointers as pointers to const data produces so much fallout that it
> does more harm than good.

Can you elaborate on the fallout, and ideally provide that rationale in
the commit message?

- Josh Triplett
--
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ard Biesheuvel Sept. 7, 2014, 5:29 p.m. UTC | #2
On 7 September 2014 19:14, Josh Triplett <josh@joshtriplett.org> wrote:
> On Sun, Sep 07, 2014 at 02:36:53PM +0200, Ard Biesheuvel wrote:
>> This code snippet:
>>
>> static void bar(void const *arg)
>> {
>>       int (*foo)(void) = arg;
>> }
>>
>> produces the following warning:
>>
>> test.c:4:28: warning: incorrect type in initializer (different modifiers)
>> test.c:4:28:    expected int ( *foo )( ... )
>> test.c:4:28:    got void const *arg
>>
>> which is caused by the fact that the function pointer 'foo' is not annotated
>> as being a pointer to const data. However, dereferencing a function pointer
>> does not produce an lvalue, so a function pointer points to const data by
>> definition, and we should treat it accordingly.
>>
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>
> As a data point: gcc does not warn about this case either, whereas it
> does warn about "int *foo = arg".  So, this seems fine to me.
>
> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
>
>> OK, so while my v1 did solve the example case, it turns out universally treating
>> function pointers as pointers to const data produces so much fallout that it
>> does more harm than good.
>
> Can you elaborate on the fallout, and ideally provide that rationale in
> the commit message?
>

Treating a function pointer as pointer-to-const-data in every
occurrence results in sparse triggering on the inverse case, i.e.

extern int (*foo)(void);
void *arg = foo;

which is a pattern that is widely used in the kernel, arguably
incorrect (considering the nature of a function pointer) but not the
case I intended to address.

I will put something to that effect in the v3 commit message.

Cheers,
Ard.
--
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/evaluate.c b/evaluate.c
index 66556150ddac..a5a830978bda 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -1359,6 +1359,15 @@  static int compatible_assignment_types(struct expression *expr, struct symbol *t
 				typediff = "different address spaces";
 				goto Err;
 			}
+			/*
+			 * If this is a function pointer assignment, it is
+			 * actually fine to assign a pointer to const data to
+			 * it, as a function pointer points to const data
+			 * implicitly, i.e., dereferencing it does not produce
+			 * an lvalue.
+			 */
+			if (b1->type == SYM_FN)
+				mod1 |= MOD_CONST;
 			if (mod2 & ~mod1) {
 				typediff = "different modifiers";
 				goto Err;