Commit b20780d6 authored by Kirill Tkhai's avatar Kirill Tkhai Committed by Pavel Emelyanov

files: Fix test and set endianess problem

Andrew Vagin reported the problem found by a checker:

    CID 174702 (#1 of 1): Out-of-bounds access (INCOMPATIBLE_CAST)
    incompatible_cast: Pointer &f->raw.counter points to an object whose
    effective type is int (32 bits, signed) but is dereferenced as a wider
    unsigned long (64 bits, unsigned). This may lead to memory corruption.

It looks like, this points to real problem, which may happen on big-endian
platforms. In the code I relay on the fact, that FDS_EVENT_BIT has a small
number and the value, it determines, fits into int type without problems.
But it's correct only for little-endian.

In case of big-endian, if the word size is 8 bytes, then FDS_EVENT value
is in the last bytes, so there is an access to wrong memory.

To fix the problem, I suggest to use little-endian byte order to work
with task_st futex. Then, the bits from 0 to 31 will be in the low adresses,
i.e. in task_st futex. There is new primitives test_and_set_bit_le() and
set_bit_le() borrowed from the linux kernel for that.

This fixes the problem, but I suppose, the checker does not see the problem
so deep, and just compares the types size, so it will fail again.
So, let's enlarge the bit field size to silence it.
Signed-off-by: 's avatarKirill Tkhai <ktkhai@virtuozzo.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
parent 74a87aba
...@@ -156,30 +156,33 @@ out: ...@@ -156,30 +156,33 @@ out:
int set_fds_event(pid_t virt) int set_fds_event(pid_t virt)
{ {
struct pstree_item *item; struct pstree_item *item;
int old; bool is_set;
item = pstree_item_by_virt(virt); item = pstree_item_by_virt(virt);
BUG_ON(!item); BUG_ON(!item);
old = test_and_set_bit(FDS_EVENT_BIT, (unsigned long *)&item->task_st); is_set = !!test_and_set_bit_le(FDS_EVENT_BIT, &item->task_st_le_bits);
if (!(old & FDS_EVENT)) if (!is_set)
futex_wake(&item->task_st); futex_wake(&item->task_st);
return 0; return 0;
} }
void clear_fds_event(void) void clear_fds_event(void)
{ {
futex_t *f = &current->task_st; clear_bit_le(FDS_EVENT_BIT, &current->task_st_le_bits);
clear_bit(FDS_EVENT_BIT, (unsigned long *)&f->raw.counter);
} }
void wait_fds_event(void) void wait_fds_event(void)
{ {
futex_t *f = &current->task_st; futex_t *f = &current->task_st;
int value;
futex_wait_if_cond(f, FDS_EVENT, &); #if BITS_PER_LONG == 64
value = htole64(FDS_EVENT);
#else
value = htole32(FDS_EVENT);
#endif
futex_wait_if_cond(f, value, &);
clear_fds_event(); clear_fds_event();
} }
......
...@@ -25,7 +25,10 @@ struct pstree_item { ...@@ -25,7 +25,10 @@ struct pstree_item {
struct pid *threads; /* array of threads */ struct pid *threads; /* array of threads */
CoreEntry **core; CoreEntry **core;
TaskKobjIdsEntry *ids; TaskKobjIdsEntry *ids;
futex_t task_st; union {
futex_t task_st;
unsigned long task_st_le_bits;
};
}; };
enum { enum {
......
#ifndef __CR_COMMON_BITOPS_H__ #ifndef __CR_COMMON_BITOPS_H__
#define __CR_COMMON_BITOPS_H__ #define __CR_COMMON_BITOPS_H__
#include "common/asm/bitops.h" #include "common/asm/bitops.h"
#include "common/bitsperlong.h"
#include <endian.h>
#if __BYTE_ORDER == __BIG_ENDIAN
#define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7)
#else
#define BITOP_LE_SWIZZLE 0
#endif
static inline int test_and_set_bit_le(int nr, void *addr)
{
return test_and_set_bit(nr ^ BITOP_LE_SWIZZLE, addr);
}
static inline void clear_bit_le(int nr, void *addr)
{
clear_bit(nr ^ BITOP_LE_SWIZZLE, addr);
}
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment