Skip to content

Commit 5512131

Browse files
author
Anselm Kruis
committed
Stackless issue python#143: fix non-portable usage of bit fields
- avoid type punning / strict aliasing violations - don't rely on implementation defined placement of bit-fields in the storage unit. (cherry picked from commit b9c243a)
1 parent cc8caca commit 5512131

8 files changed

Lines changed: 151 additions & 24 deletions

File tree

‎Stackless/core/stackless_impl.h‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,21 @@ extern "C" {
4343
* This would usually be done in place with the assembly macros.
4444
*/
4545

46+
/*
47+
* Macros used to extract bit-field values from an integer in a portable
48+
* way.
49+
*/
50+
#define SLP_EXTRACT_BITS_(integer, bits, offset) \
51+
(((integer) & (((1U << (bits))-1U) << (offset))) >> (offset))
52+
53+
#define SLP_SET_BITFIELD(NAME_PREFIX, bitfield_struct, integer, member) \
54+
(bitfield_struct).member = SLP_EXTRACT_BITS_((integer), NAME_PREFIX ## _BITS_ ## member, NAME_PREFIX ## _OFFSET_ ## member)
55+
56+
#define SLP_PREPARE_BITS_(value, offset) \
57+
(((unsigned int)(value)) << (offset))
4658

59+
#define SLP_GET_BITFIELD(NAME_PREFIX, bitfield_struct, member) \
60+
SLP_PREPARE_BITS_((bitfield_struct).member, NAME_PREFIX ## _OFFSET_ ## member)
4761

4862
/*
4963
* About the reference counting of frames and C-frames
@@ -681,10 +695,10 @@ do { \
681695
/* ditto, without incref. Made no sense to optimize. */
682696
#if defined(__GNUC__) && defined(__STDC__) && (__STDC_VERSION__ >= 199901L)
683697
#define SLP_DISABLE_GCC_W_ADDRESS \
684-
_Pragma("GCC diagnostic push") \
685-
_Pragma("GCC diagnostic ignored \"-Waddress\"")
698+
_Pragma("GCC diagnostic push") \
699+
_Pragma("GCC diagnostic ignored \"-Waddress\"")
686700
#define SLP_RESTORE_WARNINGS \
687-
_Pragma("GCC diagnostic pop")
701+
_Pragma("GCC diagnostic pop")
688702
#else
689703
#define SLP_DISABLE_GCC_W_ADDRESS /**/
690704
#define SLP_RESTORE_WARNINGS /**/

‎Stackless/core/stackless_structs.h‎

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,36 @@ extern "C" {
6262
6363
***************************************************************************/
6464

65+
#define SLP_TASKLET_FLAGS_BITS_blocked 2
66+
#define SLP_TASKLET_FLAGS_BITS_atomic 1
67+
#define SLP_TASKLET_FLAGS_BITS_ignore_nesting 1
68+
#define SLP_TASKLET_FLAGS_BITS_autoschedule 1
69+
#define SLP_TASKLET_FLAGS_BITS_block_trap 1
70+
#define SLP_TASKLET_FLAGS_BITS_is_zombie 1
71+
#define SLP_TASKLET_FLAGS_BITS_pending_irq 1
72+
73+
#define SLP_TASKLET_FLAGS_OFFSET_blocked 0
74+
#define SLP_TASKLET_FLAGS_OFFSET_atomic \
75+
(SLP_TASKLET_FLAGS_OFFSET_blocked + SLP_TASKLET_FLAGS_BITS_blocked)
76+
#define SLP_TASKLET_FLAGS_OFFSET_ignore_nesting \
77+
(SLP_TASKLET_FLAGS_OFFSET_atomic + SLP_TASKLET_FLAGS_BITS_atomic)
78+
#define SLP_TASKLET_FLAGS_OFFSET_autoschedule \
79+
(SLP_TASKLET_FLAGS_OFFSET_ignore_nesting + SLP_TASKLET_FLAGS_BITS_ignore_nesting)
80+
#define SLP_TASKLET_FLAGS_OFFSET_block_trap \
81+
(SLP_TASKLET_FLAGS_OFFSET_autoschedule + SLP_TASKLET_FLAGS_BITS_autoschedule)
82+
#define SLP_TASKLET_FLAGS_OFFSET_is_zombie \
83+
(SLP_TASKLET_FLAGS_OFFSET_block_trap + SLP_TASKLET_FLAGS_BITS_block_trap)
84+
#define SLP_TASKLET_FLAGS_OFFSET_pending_irq \
85+
(SLP_TASKLET_FLAGS_OFFSET_is_zombie + SLP_TASKLET_FLAGS_BITS_is_zombie)
86+
6587
typedef struct _tasklet_flags {
66-
int blocked: 2;
67-
unsigned int atomic: 1;
68-
unsigned int ignore_nesting: 1;
69-
unsigned int autoschedule: 1;
70-
unsigned int block_trap: 1;
71-
unsigned int is_zombie: 1;
72-
unsigned int pending_irq: 1;
88+
signed int blocked: SLP_TASKLET_FLAGS_BITS_blocked;
89+
unsigned int atomic: SLP_TASKLET_FLAGS_BITS_atomic;
90+
unsigned int ignore_nesting: SLP_TASKLET_FLAGS_BITS_ignore_nesting;
91+
unsigned int autoschedule: SLP_TASKLET_FLAGS_BITS_autoschedule;
92+
unsigned int block_trap: SLP_TASKLET_FLAGS_BITS_block_trap;
93+
unsigned int is_zombie: SLP_TASKLET_FLAGS_BITS_is_zombie;
94+
unsigned int pending_irq: SLP_TASKLET_FLAGS_BITS_pending_irq;
7395
} PyTaskletFlagStruc;
7496

7597
/* a partial copy of PyThreadState. Used to preserve
@@ -160,10 +182,20 @@ typedef struct _bomb {
160182
161183
***************************************************************************/
162184

185+
#define SLP_CHANNEL_FLAGS_BITS_closing 1
186+
#define SLP_CHANNEL_FLAGS_BITS_preference 2
187+
#define SLP_CHANNEL_FLAGS_BITS_schedule_all 1
188+
189+
#define SLP_CHANNEL_FLAGS_OFFSET_closing 0
190+
#define SLP_CHANNEL_FLAGS_OFFSET_preference \
191+
(SLP_CHANNEL_FLAGS_OFFSET_closing + SLP_CHANNEL_FLAGS_BITS_closing)
192+
#define SLP_CHANNEL_FLAGS_OFFSET_schedule_all \
193+
(SLP_CHANNEL_FLAGS_OFFSET_preference + SLP_CHANNEL_FLAGS_BITS_preference)
194+
163195
typedef struct _channel_flags {
164-
unsigned int closing: 1;
165-
int preference: 2;
166-
unsigned int schedule_all:1;
196+
unsigned int closing: SLP_CHANNEL_FLAGS_BITS_closing;
197+
signed int preference: SLP_CHANNEL_FLAGS_BITS_preference;
198+
unsigned int schedule_all: SLP_CHANNEL_FLAGS_BITS_schedule_all;
167199
} PyChannelFlagStruc;
168200

169201
typedef struct _channel {

‎Stackless/module/channelobject.c‎

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,37 @@
1313
static void
1414
channel_remove_all(PyObject *ob);
1515

16+
Py_LOCAL_INLINE(PyChannelFlagStruc)
17+
channel_flags_from_integer(int flags) {
18+
#if defined(SLP_USE_NATIVE_BITFIELD_LAYOUT) && SLP_USE_NATIVE_BITFIELD_LAYOUT
19+
PyChannelFlagStruc f;
20+
Py_MEMCPY(&f, &flags, sizeof(f));
21+
#else
22+
/* the portable way */
23+
PyChannelFlagStruc f = {0, };
24+
SLP_SET_BITFIELD(SLP_CHANNEL_FLAGS, f, flags, closing);
25+
SLP_SET_BITFIELD(SLP_CHANNEL_FLAGS, f, flags, preference);
26+
SLP_SET_BITFIELD(SLP_CHANNEL_FLAGS, f, flags, schedule_all);
27+
#endif
28+
(void) Py_BUILD_ASSERT_EXPR(sizeof(f) == sizeof(flags));
29+
return f;
30+
}
31+
32+
Py_LOCAL_INLINE(int)
33+
channel_flags_as_integer(PyChannelFlagStruc flags) {
34+
int f;
35+
(void) Py_BUILD_ASSERT_EXPR(sizeof(f) == sizeof(flags));
36+
#if defined(SLP_USE_NATIVE_BITFIELD_LAYOUT) && SLP_USE_NATIVE_BITFIELD_LAYOUT
37+
Py_MEMCPY(&f, &flags, sizeof(f));
38+
#else
39+
/* the portable way */
40+
f = SLP_GET_BITFIELD(SLP_CHANNEL_FLAGS, flags, closing) |
41+
SLP_GET_BITFIELD(SLP_CHANNEL_FLAGS, flags, preference) |
42+
SLP_GET_BITFIELD(SLP_CHANNEL_FLAGS, flags, schedule_all);
43+
#endif
44+
return f;
45+
}
46+
1647
/* GC support. The tasklets already know if they are collectable
1748
* or not. If they are not, and referenced by the channel, then
1849
* no channel_clear() will be performed. Thus, the GC support
@@ -163,7 +194,7 @@ PyChannel_New(PyTypeObject *type)
163194
c->head = c->tail = (PyTaskletObject *) c;
164195
c->balance = 0;
165196
c->chan_weakreflist = NULL;
166-
*(int*)&c->flags = 0;
197+
memset(&c->flags, 0, sizeof(c->flags));
167198
c->flags.preference = -1; /* default fast receive */
168199
}
169200
return c;
@@ -1089,7 +1120,7 @@ channel_reduce(PyChannelObject * ch)
10891120
tup = Py_BuildValue("(O()(iiO))",
10901121
Py_TYPE(ch),
10911122
ch->balance,
1092-
ch->flags,
1123+
channel_flags_as_integer(ch->flags),
10931124
lis
10941125
);
10951126
err_exit:
@@ -1118,7 +1149,7 @@ channel_setstate(PyObject *self, PyObject *args)
11181149

11191150
channel_remove_all((PyObject *) ch);
11201151
n = PyList_GET_SIZE(lis);
1121-
*(int *)&ch->flags = flags;
1152+
ch->flags = channel_flags_from_integer(flags);
11221153
dir = balance > 0 ? 1 : -1;
11231154

11241155
for (i = 0; i < n; i++) {

‎Stackless/module/taskletobject.c‎

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,48 @@
99
#ifdef STACKLESS
1010
#include "core/stackless_impl.h"
1111

12+
/*
13+
* Convert C-bitfield
14+
*/
15+
Py_LOCAL_INLINE(PyTaskletFlagStruc)
16+
tasklet_flags_from_integer(int flags) {
17+
#if defined(SLP_USE_NATIVE_BITFIELD_LAYOUT) && SLP_USE_NATIVE_BITFIELD_LAYOUT
18+
PyTaskletFlagStruc f;
19+
Py_MEMCPY(&f, &flags, sizeof(f));
20+
#else
21+
/* the portable way */
22+
PyTaskletFlagStruc f = {0, };
23+
SLP_SET_BITFIELD(SLP_TASKLET_FLAGS, f, flags, blocked);
24+
SLP_SET_BITFIELD(SLP_TASKLET_FLAGS, f, flags, atomic);
25+
SLP_SET_BITFIELD(SLP_TASKLET_FLAGS, f, flags, ignore_nesting);
26+
SLP_SET_BITFIELD(SLP_TASKLET_FLAGS, f, flags, autoschedule);
27+
SLP_SET_BITFIELD(SLP_TASKLET_FLAGS, f, flags, block_trap);
28+
SLP_SET_BITFIELD(SLP_TASKLET_FLAGS, f, flags, is_zombie);
29+
SLP_SET_BITFIELD(SLP_TASKLET_FLAGS, f, flags, pending_irq);
30+
#endif
31+
(void) Py_BUILD_ASSERT_EXPR(sizeof(f) == sizeof(flags));
32+
return f;
33+
}
34+
35+
Py_LOCAL_INLINE(int)
36+
tasklet_flags_as_integer(PyTaskletFlagStruc flags) {
37+
int f;
38+
(void) Py_BUILD_ASSERT_EXPR(sizeof(f) == sizeof(flags));
39+
#if defined(SLP_USE_NATIVE_BITFIELD_LAYOUT) && SLP_USE_NATIVE_BITFIELD_LAYOUT
40+
Py_MEMCPY(&f, &flags, sizeof(f));
41+
#else
42+
/* the portable way */
43+
f = SLP_GET_BITFIELD(SLP_TASKLET_FLAGS, flags, blocked) |
44+
SLP_GET_BITFIELD(SLP_TASKLET_FLAGS, flags, atomic) |
45+
SLP_GET_BITFIELD(SLP_TASKLET_FLAGS, flags, ignore_nesting) |
46+
SLP_GET_BITFIELD(SLP_TASKLET_FLAGS, flags, autoschedule) |
47+
SLP_GET_BITFIELD(SLP_TASKLET_FLAGS, flags, block_trap) |
48+
SLP_GET_BITFIELD(SLP_TASKLET_FLAGS, flags, is_zombie) |
49+
SLP_GET_BITFIELD(SLP_TASKLET_FLAGS, flags, pending_irq);
50+
#endif
51+
return f;
52+
}
53+
1254
void
1355
slp_current_insert(PyTaskletObject *task)
1456
{
@@ -366,7 +408,7 @@ tasklet_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
366408
t = (PyTaskletObject *) type->tp_alloc(type, 0);
367409
if (t == NULL)
368410
return NULL;
369-
*(int*)&t->flags = 0;
411+
memset(&t->flags, 0, sizeof(t->flags));
370412
t->recursion_depth = 0;
371413
t->next = NULL;
372414
t->prev = NULL;
@@ -460,7 +502,7 @@ tasklet_reduce(PyTaskletObject * t)
460502
assert(t->cstate != NULL);
461503
tup = Py_BuildValue("(O()(" TASKLET_TUPLEFMT "))",
462504
Py_TYPE(t),
463-
t->flags,
505+
tasklet_flags_as_integer(t->flags),
464506
t->tempval,
465507
t->cstate->nesting_level,
466508
lis
@@ -513,7 +555,7 @@ tasklet_setstate(PyObject *self, PyObject *args)
513555
* channel would have set it.
514556
*/
515557
j = t->flags.blocked;
516-
*(int *)&t->flags = flags;
558+
t->flags = tasklet_flags_from_integer(flags);
517559
if (t->next == NULL) {
518560
t->flags.blocked = 0;
519561
} else {

‎Stackless/platf/switch_amd64_unix.h‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
* Ported from i386.
3030
*/
3131

32+
#define SLP_USE_NATIVE_BITFIELD_LAYOUT 1
33+
3234
#define STACK_REFPLUS 1
3335

3436
#ifdef SLP_EVAL
@@ -86,7 +88,7 @@
8688
/* Maximum possible clobber list. It gives the same assembly code as the minimum list.
8789
If the ABI evolves, it might be necessary to add some of these registers */
8890
#define REGS_CLOBBERED "memory", "rax", "rbx", "rcx", "rdx", "rsi", "rdi", \
89-
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", \
91+
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", \
9092
"st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", \
9193
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", \
9294
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
@@ -130,13 +132,13 @@ slp_switch(void)
130132
SLP_RESTORE_STATE();
131133
#if STACKLESS_FRHACK
132134
__asm__ volatile (
133-
"ldmxcsr %1\n\t"
135+
"ldmxcsr %1\n\t"
134136
"fldcw %0\n\t"
135137
: : "m" (x87cw), "m" (mxcsr));
136138
#else
137139
__asm__ volatile (
138140
"movq %2, %%rbp\n\t"
139-
"ldmxcsr %1\n\t"
141+
"ldmxcsr %1\n\t"
140142
"fldcw %0\n\t"
141143
: : "m" (x87cw), "m" (mxcsr), "m" (rbp));
142144
#endif

‎Stackless/platf/switch_x64_msvc.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
* Initial final version after lots of iterations for i386.
2323
*/
2424

25+
#define SLP_USE_NATIVE_BITFIELD_LAYOUT 1
26+
2527
#define alloca _alloca
2628

2729
#define STACK_REFPLUS 1

‎Stackless/platf/switch_x86_msvc.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
* Initial final version after lots of iterations for i386.
2323
*/
2424

25+
#define SLP_USE_NATIVE_BITFIELD_LAYOUT 1
26+
2527
/* for the SEH things */
2628
#ifndef _WINDOWS_
2729
#define WIN32_LEAN_AND_MEAN

‎Stackless/platf/switch_x86_unix.h‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
* Ported from i386.
2222
*/
2323

24+
#define SLP_USE_NATIVE_BITFIELD_LAYOUT 1
25+
2426
#define STACK_REFPLUS 1
2527

2628
#ifdef SLP_EVAL
@@ -78,12 +80,12 @@ slp_switch(void)
7880
register int *stackref, stsizediff;
7981
#if STACKLESS_FRHACK
8082
__asm__ volatile (
81-
""
83+
""
8284
: : : REGS_CLOBBERED );
8385
#else
8486
void * ebp;
8587
__asm__ volatile (
86-
"movl %%ebp, %0\n\t"
88+
"movl %%ebp, %0\n\t"
8789
: "=m" (ebp) : : REGS_CLOBBERED );
8890
#endif
8991
__asm__ ("movl %%esp, %0" : "=g" (stackref));

0 commit comments

Comments
 (0)