summary refs log tree commit diff stats
path: root/results/classifier/user-mode-bugs/616
blob: 4bd3bac28e33494b23bb3590fbd76d93be27f154 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
overflow condition code determined incorrectly after addition on s390x
Description of problem:
The following program foo.c
[foo.c](/uploads/78f5f799af6e3c400a6a42634f3f0e63/foo.c)

```
#include <stdio.h>

int overflow_32 (int x, int y)
{
  int sum;
  return ! __builtin_add_overflow (x, y, &sum);
}

int overflow_64 (long long x, long long y)
{
  long sum;
  return ! __builtin_add_overflow (x, y, &sum);
}

int a1 = -2147483648;
int b1 = -2147483648;
long long a2 = -9223372036854775808L;
long long b2 = -9223372036854775808L;

int main ()
{
  {
    int a = a1;
    int b = b1;
    printf ("a = 0x%x, b = 0x%x\n", a, b);
    printf ("no_overflow = %d\n", overflow_32 (a, b));
  }
  {
    long long a = a2;
    long long b = b2;
    printf ("a = 0x%llx, b = 0x%llx\n", a, b);
    printf ("no_overflow = %d\n", overflow_64 (a, b));
  }
}
```

should print

```
a = 0x80000000, b = 0x80000000
no_overflow = 0
a = 0x8000000000000000, b = 0x8000000000000000
no_overflow = 0
```

However, when compiled as an s390x program and executed through
qemu 6.1.0 (Linux user-mode), it prints 'no_overflow = 1' twice.

```
$ s390x-linux-gnu-gcc-10 --version
s390x-linux-gnu-gcc-10 (Ubuntu 10.3.0-1ubuntu1~20.04) 10.3.0
```

```
$ s390x-linux-gnu-gcc-10 -static foo.c
$ ~/inst-qemu/6.1.0/bin/qemu-s390x a.out
a = 0x80000000, b = 0x80000000
no_overflow = 1
a = 0x8000000000000000, b = 0x8000000000000000
no_overflow = 1
```

```
$ s390x-linux-gnu-gcc-10 -O2 -static foo.c
$ ~/inst-qemu/6.1.0/bin/qemu-s390x a.out
a = 0x80000000, b = 0x80000000
no_overflow = 1
a = 0x8000000000000000, b = 0x8000000000000000
no_overflow = 1
```

The code generated by 's390x-linux-gnu-gcc-10 -O2' makes use of the
'o' (overflow / ones) condition code:

```
overflow_64:
        lgr     %r1,%r2    ;; copy a into %r1
        lghi    %r2,0
        agr     %r1,%r3    ;; add a and b
        bnor    %r14       ;; if no overflow, return %r2 = 0
        lghi    %r2,1
        br      %r14       ;; otherwise, return %r2 = 1
```

Either the bug is in GCC, that is, GCC produces code that uses the CPU's
overflow condition code when it shouldn't.

Or the bug is in QEMU, that is, QEMU does not set the overflow condition
code correctly.

This can be decided by running the above program on real Linux/s390x hardware
(to which I don't have access).
Steps to reproduce:
[foo.static.s390x](/uploads/ac41abf4c54baf9ca96ba82d75a24ad6/foo.static.s390x)
(foo.static.s390x is attached, the result of "s390x-linux-gnu-gcc-10 -static -O2 foo.c -o foo.static.s390x")

1. `qemu-s390x foo.static.s390x`
Additional information:
If the bug is really in QEMU, the attached patch fixes it.

[0001-s390x-Fix-determination-of-overflow-condition-code-a.patch](/uploads/552917079ccd25f1861d682fc9dee3e8/0001-s390x-Fix-determination-of-overflow-condition-code-a.patch)