Fix ubsan 'left shift of negative value -1' error in satsub64be()
[oweals/openssl.git] / ssl / record / dtls1_bitmap.c
1 /*
2  * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include "../ssl_locl.h"
11 #include "record_locl.h"
12
13 /* mod 128 saturating subtract of two 64-bit values in big-endian order */
14 static int satsub64be(const unsigned char *v1, const unsigned char *v2)
15 {
16     int ret, i;
17
18     if (sizeof(long) == 8)
19         do {
20             const union {
21                 long one;
22                 char little;
23             } is_endian = {
24                 1
25             };
26             long l;
27
28             if (is_endian.little)
29                 break;
30             /* not reached on little-endians */
31             /*
32              * following test is redundant, because input is always aligned,
33              * but I take no chances...
34              */
35             if (((size_t)v1 | (size_t)v2) & 0x7)
36                 break;
37
38             l = *((long *)v1);
39             l -= *((long *)v2);
40             if (l > 128)
41                 return 128;
42             else if (l < -128)
43                 return -128;
44             else
45                 return (int)l;
46         } while (0);
47
48     ret = 0;
49     for (i=0; i<7; i++) {
50         if (v1[i] > v2[i]) {
51             /* v1 is larger... but by how much? */
52             if (v1[i] != v2[i] + 1)
53                 return 128;
54             while (++i <= 6) {
55                 if (v1[i] != 0x00 || v2[i] != 0xff)
56                     return 128; /* too much */
57             }
58             /* We checked all the way to the penultimate byte,
59              * so despite higher bytes changing we actually
60              * know that it only changed from (e.g.)
61              *       ... (xx)  ff ff ff ??
62              * to   ... (xx+1) 00 00 00 ??
63              * so we add a 'bias' of 256 for the carry that
64              * happened, and will eventually return
65              * 256 + v1[7] - v2[7]. */
66             ret = 256;
67             break;
68         } else if (v2[i] > v1[i]) {
69             /* v2 is larger... but by how much? */
70             if (v2[i] != v1[i] + 1)
71                 return -128;
72             while (++i <= 6) {
73                 if (v2[i] != 0x00 || v1[i] != 0xff)
74                     return -128; /* too much */
75             }
76             /* Similar to the case above, we know it changed
77              * from    ... (xx)  00 00 00 ??
78              * to     ... (xx-1) ff ff ff ??
79              * so we add a 'bias' of -256 for the borrow,
80              * to return -256 + v1[7] - v2[7]. */
81             ret = -256;
82         }
83     }
84
85     ret += (int)v1[7] - (int)v2[7];
86
87     if (ret > 128)
88         return 128;
89     else if (ret < -128)
90         return -128;
91     else
92         return ret;
93 }
94
95 int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap)
96 {
97     int cmp;
98     unsigned int shift;
99     const unsigned char *seq = s->rlayer.read_sequence;
100
101     cmp = satsub64be(seq, bitmap->max_seq_num);
102     if (cmp > 0) {
103         SSL3_RECORD_set_seq_num(RECORD_LAYER_get_rrec(&s->rlayer), seq);
104         return 1;               /* this record in new */
105     }
106     shift = -cmp;
107     if (shift >= sizeof(bitmap->map) * 8)
108         return 0;               /* stale, outside the window */
109     else if (bitmap->map & (1UL << shift))
110         return 0;               /* record previously received */
111
112     SSL3_RECORD_set_seq_num(RECORD_LAYER_get_rrec(&s->rlayer), seq);
113     return 1;
114 }
115
116 void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap)
117 {
118     int cmp;
119     unsigned int shift;
120     const unsigned char *seq = RECORD_LAYER_get_read_sequence(&s->rlayer);
121
122     cmp = satsub64be(seq, bitmap->max_seq_num);
123     if (cmp > 0) {
124         shift = cmp;
125         if (shift < sizeof(bitmap->map) * 8)
126             bitmap->map <<= shift, bitmap->map |= 1UL;
127         else
128             bitmap->map = 1UL;
129         memcpy(bitmap->max_seq_num, seq, SEQ_NUM_SIZE);
130     } else {
131         shift = -cmp;
132         if (shift < sizeof(bitmap->map) * 8)
133             bitmap->map |= 1UL << shift;
134     }
135 }