Skip to content

Commit 3c8ba0d

Browse files
keestorvalds
authored andcommitted
kernel.h: Retain constant expression output for max()/min()
In the effort to remove all VLAs from the kernel[1], it is desirable to build with -Wvla. However, this warning is overly pessimistic, in that it is only happy with stack array sizes that are declared as constant expressions, and not constant values. One case of this is the evaluation of the max() macro which, due to its construction, ends up converting constant expression arguments into a constant value result. All attempts to rewrite this macro with __builtin_constant_p() failed with older compilers (e.g. gcc 4.4)[2]. However, Martin Uecker, constructed[3] a mind-shattering solution that works everywhere. Cthulhu fhtagn! This patch updates the min()/max() macros to evaluate to a constant expression when called on constant expression arguments. This removes several false-positive stack VLA warnings from an x86 allmodconfig build when -Wvla is added: $ diff -u before.txt after.txt | grep ^- -drivers/input/touchscreen/cyttsp4_core.c:871:2: warning: ISO C90 forbids variable length array ‘ids’ [-Wvla] -fs/btrfs/tree-checker.c:344:4: warning: ISO C90 forbids variable length array ‘namebuf’ [-Wvla] -lib/vsprintf.c:747:2: warning: ISO C90 forbids variable length array ‘sym’ [-Wvla] -net/ipv4/proc.c:403:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla] -net/ipv6/proc.c:198:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla] -net/ipv6/proc.c:218:2: warning: ISO C90 forbids variable length array ‘buff64’ [-Wvla] This also updates two cases where different enums were being compared and explicitly casts them to int (which matches the old side-effect of the single-evaluation code): one in tpm/tpm_tis_core.h, and one in drm/drm_color_mgmt.c. [1] https://lkml.org/lkml/2018/3/7/621 [2] https://lkml.org/lkml/2018/3/10/170 [3] https://lkml.org/lkml/2018/3/20/845 Co-Developed-by: Linus Torvalds <[email protected]> Co-Developed-by: Martin Uecker <[email protected]> Signed-off-by: Kees Cook <[email protected]> Acked-by: Ingo Molnar <[email protected]> Acked-by: Miguel Ojeda <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5414ab3 commit 3c8ba0d

File tree

3 files changed

+47
-36
lines changed

3 files changed

+47
-36
lines changed

drivers/char/tpm/tpm_tis_core.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ enum tis_defaults {
6262
/* Some timeout values are needed before it is known whether the chip is
6363
* TPM 1.0 or TPM 2.0.
6464
*/
65-
#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
66-
#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
67-
#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
68-
#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
65+
#define TIS_TIMEOUT_A_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
66+
#define TIS_TIMEOUT_B_MAX max_t(int, TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
67+
#define TIS_TIMEOUT_C_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
68+
#define TIS_TIMEOUT_D_MAX max_t(int, TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
6969

7070
#define TPM_ACCESS(l) (0x0000 | ((l) << 12))
7171
#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))

drivers/gpu/drm/drm_color_mgmt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,8 @@ int drm_plane_create_color_properties(struct drm_plane *plane,
417417
{
418418
struct drm_device *dev = plane->dev;
419419
struct drm_property *prop;
420-
struct drm_prop_enum_list enum_list[max(DRM_COLOR_ENCODING_MAX,
421-
DRM_COLOR_RANGE_MAX)];
420+
struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX,
421+
DRM_COLOR_RANGE_MAX)];
422422
int i, len;
423423

424424
if (WARN_ON(supported_encodings == 0 ||

include/linux/kernel.h

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -792,41 +792,58 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
792792
#endif /* CONFIG_TRACING */
793793

794794
/*
795-
* min()/max()/clamp() macros that also do
796-
* strict type-checking.. See the
797-
* "unnecessary" pointer comparison.
795+
* min()/max()/clamp() macros must accomplish three things:
796+
*
797+
* - avoid multiple evaluations of the arguments (so side-effects like
798+
* "x++" happen only once) when non-constant.
799+
* - perform strict type-checking (to generate warnings instead of
800+
* nasty runtime surprises). See the "unnecessary" pointer comparison
801+
* in __typecheck().
802+
* - retain result as a constant expressions when called with only
803+
* constant expressions (to avoid tripping VLA warnings in stack
804+
* allocation usage).
805+
*/
806+
#define __typecheck(x, y) \
807+
(!!(sizeof((typeof(x) *)1 == (typeof(y) *)1)))
808+
809+
/*
810+
* This returns a constant expression while determining if an argument is
811+
* a constant expression, most importantly without evaluating the argument.
812+
* Glory to Martin Uecker <[email protected]>
798813
*/
799-
#define __min(t1, t2, min1, min2, x, y) ({ \
800-
t1 min1 = (x); \
801-
t2 min2 = (y); \
802-
(void) (&min1 == &min2); \
803-
min1 < min2 ? min1 : min2; })
814+
#define __is_constexpr(x) \
815+
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
816+
817+
#define __no_side_effects(x, y) \
818+
(__is_constexpr(x) && __is_constexpr(y))
819+
820+
#define __safe_cmp(x, y) \
821+
(__typecheck(x, y) && __no_side_effects(x, y))
822+
823+
#define __cmp(x, y, op) ((x) op (y) ? (x) : (y))
824+
825+
#define __cmp_once(x, y, op) ({ \
826+
typeof(x) __x = (x); \
827+
typeof(y) __y = (y); \
828+
__cmp(__x, __y, op); })
829+
830+
#define __careful_cmp(x, y, op) \
831+
__builtin_choose_expr(__safe_cmp(x, y), \
832+
__cmp(x, y, op), __cmp_once(x, y, op))
804833

805834
/**
806835
* min - return minimum of two values of the same or compatible types
807836
* @x: first value
808837
* @y: second value
809838
*/
810-
#define min(x, y) \
811-
__min(typeof(x), typeof(y), \
812-
__UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \
813-
x, y)
814-
815-
#define __max(t1, t2, max1, max2, x, y) ({ \
816-
t1 max1 = (x); \
817-
t2 max2 = (y); \
818-
(void) (&max1 == &max2); \
819-
max1 > max2 ? max1 : max2; })
839+
#define min(x, y) __careful_cmp(x, y, <)
820840

821841
/**
822842
* max - return maximum of two values of the same or compatible types
823843
* @x: first value
824844
* @y: second value
825845
*/
826-
#define max(x, y) \
827-
__max(typeof(x), typeof(y), \
828-
__UNIQUE_ID(max1_), __UNIQUE_ID(max2_), \
829-
x, y)
846+
#define max(x, y) __careful_cmp(x, y, >)
830847

831848
/**
832849
* min3 - return minimum of three values
@@ -878,21 +895,15 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
878895
* @x: first value
879896
* @y: second value
880897
*/
881-
#define min_t(type, x, y) \
882-
__min(type, type, \
883-
__UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \
884-
x, y)
898+
#define min_t(type, x, y) __careful_cmp((type)(x), (type)(y), <)
885899

886900
/**
887901
* max_t - return maximum of two values, using the specified type
888902
* @type: data type to use
889903
* @x: first value
890904
* @y: second value
891905
*/
892-
#define max_t(type, x, y) \
893-
__max(type, type, \
894-
__UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \
895-
x, y)
906+
#define max_t(type, x, y) __careful_cmp((type)(x), (type)(y), >)
896907

897908
/**
898909
* clamp_t - return a value clamped to a given range using a given type

0 commit comments

Comments
 (0)