diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-21 15:32:08 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-21 15:32:08 -0500 |
commit | eae21770b4fed5597623aad0d618190fa60426ff (patch) | |
tree | 23c59fb7a33e93a79525e2b10d56df54d40049d1 /lib | |
parent | e9f57ebcba563e0cd532926cab83c92bb4d79360 (diff) | |
parent | 9f273c24ec5f4a6f785bb83e931b3808a07b459e (diff) |
Merge branch 'akpm' (patches from Andrew)
Merge third patch-bomb from Andrew Morton:
"I'm pretty much done for -rc1 now:
- the rest of MM, basically
- lib/ updates
- checkpatch, epoll, hfs, fatfs, ptrace, coredump, exit
- cpu_mask simplifications
- kexec, rapidio, MAINTAINERS etc, etc.
- more dma-mapping cleanups/simplifications from hch"
* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (109 commits)
MAINTAINERS: add/fix git URLs for various subsystems
mm: memcontrol: add "sock" to cgroup2 memory.stat
mm: memcontrol: basic memory statistics in cgroup2 memory controller
mm: memcontrol: do not uncharge old page in page cache replacement
Documentation: cgroup: add memory.swap.{current,max} description
mm: free swap cache aggressively if memcg swap is full
mm: vmscan: do not scan anon pages if memcg swap limit is hit
swap.h: move memcg related stuff to the end of the file
mm: memcontrol: replace mem_cgroup_lruvec_online with mem_cgroup_online
mm: vmscan: pass memcg to get_scan_count()
mm: memcontrol: charge swap to cgroup2
mm: memcontrol: clean up alloc, online, offline, free functions
mm: memcontrol: flatten struct cg_proto
mm: memcontrol: rein in the CONFIG space madness
net: drop tcp_memcontrol.c
mm: memcontrol: introduce CONFIG_MEMCG_LEGACY_KMEM
mm: memcontrol: allow to disable kmem accounting for cgroup2
mm: memcontrol: account "kmem" consumers in cgroup2 memory controller
mm: memcontrol: move kmem accounting code to CONFIG_MEMCG
mm: memcontrol: separate kmem code from legacy tcp accounting code
...
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Kconfig.debug | 2 | ||||
-rw-r--r-- | lib/Kconfig.ubsan | 29 | ||||
-rw-r--r-- | lib/Makefile | 7 | ||||
-rw-r--r-- | lib/iomap_copy.c | 21 | ||||
-rw-r--r-- | lib/libcrc32c.c | 1 | ||||
-rw-r--r-- | lib/string_helpers.c | 63 | ||||
-rw-r--r-- | lib/test_hexdump.c (renamed from lib/test-hexdump.c) | 146 | ||||
-rw-r--r-- | lib/ubsan.c | 456 | ||||
-rw-r--r-- | lib/ubsan.h | 84 |
9 files changed, 743 insertions, 66 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 7d0b49c536c5..ecb9e75614bf 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -1893,6 +1893,8 @@ source "samples/Kconfig" | |||
1893 | 1893 | ||
1894 | source "lib/Kconfig.kgdb" | 1894 | source "lib/Kconfig.kgdb" |
1895 | 1895 | ||
1896 | source "lib/Kconfig.ubsan" | ||
1897 | |||
1896 | config ARCH_HAS_DEVMEM_IS_ALLOWED | 1898 | config ARCH_HAS_DEVMEM_IS_ALLOWED |
1897 | bool | 1899 | bool |
1898 | 1900 | ||
diff --git a/lib/Kconfig.ubsan b/lib/Kconfig.ubsan new file mode 100644 index 000000000000..49518fb48cab --- /dev/null +++ b/lib/Kconfig.ubsan | |||
@@ -0,0 +1,29 @@ | |||
1 | config ARCH_HAS_UBSAN_SANITIZE_ALL | ||
2 | bool | ||
3 | |||
4 | config UBSAN | ||
5 | bool "Undefined behaviour sanity checker" | ||
6 | help | ||
7 | This option enables undefined behaviour sanity checker | ||
8 | Compile-time instrumentation is used to detect various undefined | ||
9 | behaviours in runtime. Various types of checks may be enabled | ||
10 | via boot parameter ubsan_handle (see: Documentation/ubsan.txt). | ||
11 | |||
12 | config UBSAN_SANITIZE_ALL | ||
13 | bool "Enable instrumentation for the entire kernel" | ||
14 | depends on UBSAN | ||
15 | depends on ARCH_HAS_UBSAN_SANITIZE_ALL | ||
16 | default y | ||
17 | help | ||
18 | This option activates instrumentation for the entire kernel. | ||
19 | If you don't enable this option, you have to explicitly specify | ||
20 | UBSAN_SANITIZE := y for the files/directories you want to check for UB. | ||
21 | |||
22 | config UBSAN_ALIGNMENT | ||
23 | bool "Enable checking of pointers alignment" | ||
24 | depends on UBSAN | ||
25 | default y if !HAVE_EFFICIENT_UNALIGNED_ACCESS | ||
26 | help | ||
27 | This option enables detection of unaligned memory accesses. | ||
28 | Enabling this option on architectures that support unalligned | ||
29 | accesses may produce a lot of false positives. | ||
diff --git a/lib/Makefile b/lib/Makefile index 180dd4d0dd41..2d4bc33d09b4 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
@@ -31,7 +31,7 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ | |||
31 | obj-y += string_helpers.o | 31 | obj-y += string_helpers.o |
32 | obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o | 32 | obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o |
33 | obj-y += hexdump.o | 33 | obj-y += hexdump.o |
34 | obj-$(CONFIG_TEST_HEXDUMP) += test-hexdump.o | 34 | obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o |
35 | obj-y += kstrtox.o | 35 | obj-y += kstrtox.o |
36 | obj-$(CONFIG_TEST_BPF) += test_bpf.o | 36 | obj-$(CONFIG_TEST_BPF) += test_bpf.o |
37 | obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o | 37 | obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o |
@@ -154,7 +154,7 @@ obj-$(CONFIG_GLOB) += glob.o | |||
154 | obj-$(CONFIG_MPILIB) += mpi/ | 154 | obj-$(CONFIG_MPILIB) += mpi/ |
155 | obj-$(CONFIG_SIGNATURE) += digsig.o | 155 | obj-$(CONFIG_SIGNATURE) += digsig.o |
156 | 156 | ||
157 | obj-$(CONFIG_CLZ_TAB) += clz_tab.o | 157 | lib-$(CONFIG_CLZ_TAB) += clz_tab.o |
158 | 158 | ||
159 | obj-$(CONFIG_DDR) += jedec_ddr_data.o | 159 | obj-$(CONFIG_DDR) += jedec_ddr_data.o |
160 | 160 | ||
@@ -209,3 +209,6 @@ quiet_cmd_build_OID_registry = GEN $@ | |||
209 | clean-files += oid_registry_data.c | 209 | clean-files += oid_registry_data.c |
210 | 210 | ||
211 | obj-$(CONFIG_UCS2_STRING) += ucs2_string.o | 211 | obj-$(CONFIG_UCS2_STRING) += ucs2_string.o |
212 | obj-$(CONFIG_UBSAN) += ubsan.o | ||
213 | |||
214 | UBSAN_SANITIZE_ubsan.o := n | ||
diff --git a/lib/iomap_copy.c b/lib/iomap_copy.c index 4527e751b5e0..b8f1d6cbb200 100644 --- a/lib/iomap_copy.c +++ b/lib/iomap_copy.c | |||
@@ -42,6 +42,27 @@ void __attribute__((weak)) __iowrite32_copy(void __iomem *to, | |||
42 | EXPORT_SYMBOL_GPL(__iowrite32_copy); | 42 | EXPORT_SYMBOL_GPL(__iowrite32_copy); |
43 | 43 | ||
44 | /** | 44 | /** |
45 | * __ioread32_copy - copy data from MMIO space, in 32-bit units | ||
46 | * @to: destination (must be 32-bit aligned) | ||
47 | * @from: source, in MMIO space (must be 32-bit aligned) | ||
48 | * @count: number of 32-bit quantities to copy | ||
49 | * | ||
50 | * Copy data from MMIO space to kernel space, in units of 32 bits at a | ||
51 | * time. Order of access is not guaranteed, nor is a memory barrier | ||
52 | * performed afterwards. | ||
53 | */ | ||
54 | void __ioread32_copy(void *to, const void __iomem *from, size_t count) | ||
55 | { | ||
56 | u32 *dst = to; | ||
57 | const u32 __iomem *src = from; | ||
58 | const u32 __iomem *end = src + count; | ||
59 | |||
60 | while (src < end) | ||
61 | *dst++ = __raw_readl(src++); | ||
62 | } | ||
63 | EXPORT_SYMBOL_GPL(__ioread32_copy); | ||
64 | |||
65 | /** | ||
45 | * __iowrite64_copy - copy data to MMIO space, in 64-bit or 32-bit units | 66 | * __iowrite64_copy - copy data to MMIO space, in 64-bit or 32-bit units |
46 | * @to: destination, in MMIO space (must be 64-bit aligned) | 67 | * @to: destination, in MMIO space (must be 64-bit aligned) |
47 | * @from: source (must be 64-bit aligned) | 68 | * @from: source (must be 64-bit aligned) |
diff --git a/lib/libcrc32c.c b/lib/libcrc32c.c index 6a08ce7d6adc..31ce853fbfb1 100644 --- a/lib/libcrc32c.c +++ b/lib/libcrc32c.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/init.h> | 36 | #include <linux/init.h> |
37 | #include <linux/kernel.h> | 37 | #include <linux/kernel.h> |
38 | #include <linux/module.h> | 38 | #include <linux/module.h> |
39 | #include <linux/crc32c.h> | ||
39 | 40 | ||
40 | static struct crypto_shash *tfm; | 41 | static struct crypto_shash *tfm; |
41 | 42 | ||
diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 5939f63d90cd..5c88204b6f1f 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c | |||
@@ -43,50 +43,73 @@ void string_get_size(u64 size, u64 blk_size, const enum string_size_units units, | |||
43 | [STRING_UNITS_10] = 1000, | 43 | [STRING_UNITS_10] = 1000, |
44 | [STRING_UNITS_2] = 1024, | 44 | [STRING_UNITS_2] = 1024, |
45 | }; | 45 | }; |
46 | int i, j; | 46 | static const unsigned int rounding[] = { 500, 50, 5 }; |
47 | u32 remainder = 0, sf_cap, exp; | 47 | int i = 0, j; |
48 | u32 remainder = 0, sf_cap; | ||
48 | char tmp[8]; | 49 | char tmp[8]; |
49 | const char *unit; | 50 | const char *unit; |
50 | 51 | ||
51 | tmp[0] = '\0'; | 52 | tmp[0] = '\0'; |
52 | i = 0; | 53 | |
53 | if (!size) | 54 | if (blk_size == 0) |
55 | size = 0; | ||
56 | if (size == 0) | ||
54 | goto out; | 57 | goto out; |
55 | 58 | ||
56 | while (blk_size >= divisor[units]) { | 59 | /* This is Napier's algorithm. Reduce the original block size to |
57 | remainder = do_div(blk_size, divisor[units]); | 60 | * |
61 | * coefficient * divisor[units]^i | ||
62 | * | ||
63 | * we do the reduction so both coefficients are just under 32 bits so | ||
64 | * that multiplying them together won't overflow 64 bits and we keep | ||
65 | * as much precision as possible in the numbers. | ||
66 | * | ||
67 | * Note: it's safe to throw away the remainders here because all the | ||
68 | * precision is in the coefficients. | ||
69 | */ | ||
70 | while (blk_size >> 32) { | ||
71 | do_div(blk_size, divisor[units]); | ||
58 | i++; | 72 | i++; |
59 | } | 73 | } |
60 | 74 | ||
61 | exp = divisor[units] / (u32)blk_size; | 75 | while (size >> 32) { |
62 | /* | 76 | do_div(size, divisor[units]); |
63 | * size must be strictly greater than exp here to ensure that remainder | ||
64 | * is greater than divisor[units] coming out of the if below. | ||
65 | */ | ||
66 | if (size > exp) { | ||
67 | remainder = do_div(size, divisor[units]); | ||
68 | remainder *= blk_size; | ||
69 | i++; | 77 | i++; |
70 | } else { | ||
71 | remainder *= size; | ||
72 | } | 78 | } |
73 | 79 | ||
80 | /* now perform the actual multiplication keeping i as the sum of the | ||
81 | * two logarithms */ | ||
74 | size *= blk_size; | 82 | size *= blk_size; |
75 | size += remainder / divisor[units]; | ||
76 | remainder %= divisor[units]; | ||
77 | 83 | ||
84 | /* and logarithmically reduce it until it's just under the divisor */ | ||
78 | while (size >= divisor[units]) { | 85 | while (size >= divisor[units]) { |
79 | remainder = do_div(size, divisor[units]); | 86 | remainder = do_div(size, divisor[units]); |
80 | i++; | 87 | i++; |
81 | } | 88 | } |
82 | 89 | ||
90 | /* work out in j how many digits of precision we need from the | ||
91 | * remainder */ | ||
83 | sf_cap = size; | 92 | sf_cap = size; |
84 | for (j = 0; sf_cap*10 < 1000; j++) | 93 | for (j = 0; sf_cap*10 < 1000; j++) |
85 | sf_cap *= 10; | 94 | sf_cap *= 10; |
86 | 95 | ||
87 | if (j) { | 96 | if (units == STRING_UNITS_2) { |
97 | /* express the remainder as a decimal. It's currently the | ||
98 | * numerator of a fraction whose denominator is | ||
99 | * divisor[units], which is 1 << 10 for STRING_UNITS_2 */ | ||
88 | remainder *= 1000; | 100 | remainder *= 1000; |
89 | remainder /= divisor[units]; | 101 | remainder >>= 10; |
102 | } | ||
103 | |||
104 | /* add a 5 to the digit below what will be printed to ensure | ||
105 | * an arithmetical round up and carry it through to size */ | ||
106 | remainder += rounding[j]; | ||
107 | if (remainder >= 1000) { | ||
108 | remainder -= 1000; | ||
109 | size += 1; | ||
110 | } | ||
111 | |||
112 | if (j) { | ||
90 | snprintf(tmp, sizeof(tmp), ".%03u", remainder); | 113 | snprintf(tmp, sizeof(tmp), ".%03u", remainder); |
91 | tmp[j+1] = '\0'; | 114 | tmp[j+1] = '\0'; |
92 | } | 115 | } |
diff --git a/lib/test-hexdump.c b/lib/test_hexdump.c index 5241df36eedf..3f415d8101f3 100644 --- a/lib/test-hexdump.c +++ b/lib/test_hexdump.c | |||
@@ -42,19 +42,21 @@ static const char * const test_data_8_le[] __initconst = { | |||
42 | "e9ac0f9cad319ca6", "0cafb1439919d14c", | 42 | "e9ac0f9cad319ca6", "0cafb1439919d14c", |
43 | }; | 43 | }; |
44 | 44 | ||
45 | static void __init test_hexdump(size_t len, int rowsize, int groupsize, | 45 | #define FILL_CHAR '#' |
46 | bool ascii) | 46 | |
47 | static unsigned total_tests __initdata; | ||
48 | static unsigned failed_tests __initdata; | ||
49 | |||
50 | static void __init test_hexdump_prepare_test(size_t len, int rowsize, | ||
51 | int groupsize, char *test, | ||
52 | size_t testlen, bool ascii) | ||
47 | { | 53 | { |
48 | char test[32 * 3 + 2 + 32 + 1]; | ||
49 | char real[32 * 3 + 2 + 32 + 1]; | ||
50 | char *p; | 54 | char *p; |
51 | const char * const *result; | 55 | const char * const *result; |
52 | size_t l = len; | 56 | size_t l = len; |
53 | int gs = groupsize, rs = rowsize; | 57 | int gs = groupsize, rs = rowsize; |
54 | unsigned int i; | 58 | unsigned int i; |
55 | 59 | ||
56 | hex_dump_to_buffer(data_b, l, rs, gs, real, sizeof(real), ascii); | ||
57 | |||
58 | if (rs != 16 && rs != 32) | 60 | if (rs != 16 && rs != 32) |
59 | rs = 16; | 61 | rs = 16; |
60 | 62 | ||
@@ -73,8 +75,6 @@ static void __init test_hexdump(size_t len, int rowsize, int groupsize, | |||
73 | else | 75 | else |
74 | result = test_data_1_le; | 76 | result = test_data_1_le; |
75 | 77 | ||
76 | memset(test, ' ', sizeof(test)); | ||
77 | |||
78 | /* hex dump */ | 78 | /* hex dump */ |
79 | p = test; | 79 | p = test; |
80 | for (i = 0; i < l / gs; i++) { | 80 | for (i = 0; i < l / gs; i++) { |
@@ -82,24 +82,49 @@ static void __init test_hexdump(size_t len, int rowsize, int groupsize, | |||
82 | size_t amount = strlen(q); | 82 | size_t amount = strlen(q); |
83 | 83 | ||
84 | strncpy(p, q, amount); | 84 | strncpy(p, q, amount); |
85 | p += amount + 1; | 85 | p += amount; |
86 | |||
87 | *p++ = ' '; | ||
86 | } | 88 | } |
87 | if (i) | 89 | if (i) |
88 | p--; | 90 | p--; |
89 | 91 | ||
90 | /* ASCII part */ | 92 | /* ASCII part */ |
91 | if (ascii) { | 93 | if (ascii) { |
92 | p = test + rs * 2 + rs / gs + 1; | 94 | do { |
95 | *p++ = ' '; | ||
96 | } while (p < test + rs * 2 + rs / gs + 1); | ||
97 | |||
93 | strncpy(p, data_a, l); | 98 | strncpy(p, data_a, l); |
94 | p += l; | 99 | p += l; |
95 | } | 100 | } |
96 | 101 | ||
97 | *p = '\0'; | 102 | *p = '\0'; |
103 | } | ||
98 | 104 | ||
99 | if (strcmp(test, real)) { | 105 | #define TEST_HEXDUMP_BUF_SIZE (32 * 3 + 2 + 32 + 1) |
106 | |||
107 | static void __init test_hexdump(size_t len, int rowsize, int groupsize, | ||
108 | bool ascii) | ||
109 | { | ||
110 | char test[TEST_HEXDUMP_BUF_SIZE]; | ||
111 | char real[TEST_HEXDUMP_BUF_SIZE]; | ||
112 | |||
113 | total_tests++; | ||
114 | |||
115 | memset(real, FILL_CHAR, sizeof(real)); | ||
116 | hex_dump_to_buffer(data_b, len, rowsize, groupsize, real, sizeof(real), | ||
117 | ascii); | ||
118 | |||
119 | memset(test, FILL_CHAR, sizeof(test)); | ||
120 | test_hexdump_prepare_test(len, rowsize, groupsize, test, sizeof(test), | ||
121 | ascii); | ||
122 | |||
123 | if (memcmp(test, real, TEST_HEXDUMP_BUF_SIZE)) { | ||
100 | pr_err("Len: %zu row: %d group: %d\n", len, rowsize, groupsize); | 124 | pr_err("Len: %zu row: %d group: %d\n", len, rowsize, groupsize); |
101 | pr_err("Result: '%s'\n", real); | 125 | pr_err("Result: '%s'\n", real); |
102 | pr_err("Expect: '%s'\n", test); | 126 | pr_err("Expect: '%s'\n", test); |
127 | failed_tests++; | ||
103 | } | 128 | } |
104 | } | 129 | } |
105 | 130 | ||
@@ -114,52 +139,72 @@ static void __init test_hexdump_set(int rowsize, bool ascii) | |||
114 | test_hexdump(len, rowsize, 1, ascii); | 139 | test_hexdump(len, rowsize, 1, ascii); |
115 | } | 140 | } |
116 | 141 | ||
117 | static void __init test_hexdump_overflow(bool ascii) | 142 | static void __init test_hexdump_overflow(size_t buflen, size_t len, |
143 | int rowsize, int groupsize, | ||
144 | bool ascii) | ||
118 | { | 145 | { |
119 | char buf[56]; | 146 | char test[TEST_HEXDUMP_BUF_SIZE]; |
120 | const char *t = test_data_1_le[0]; | 147 | char buf[TEST_HEXDUMP_BUF_SIZE]; |
121 | size_t l = get_random_int() % sizeof(buf); | 148 | int rs = rowsize, gs = groupsize; |
149 | int ae, he, e, f, r; | ||
122 | bool a; | 150 | bool a; |
123 | int e, r; | ||
124 | 151 | ||
125 | memset(buf, ' ', sizeof(buf)); | 152 | total_tests++; |
153 | |||
154 | memset(buf, FILL_CHAR, sizeof(buf)); | ||
126 | 155 | ||
127 | r = hex_dump_to_buffer(data_b, 1, 16, 1, buf, l, ascii); | 156 | r = hex_dump_to_buffer(data_b, len, rs, gs, buf, buflen, ascii); |
157 | |||
158 | /* | ||
159 | * Caller must provide the data length multiple of groupsize. The | ||
160 | * calculations below are made with that assumption in mind. | ||
161 | */ | ||
162 | ae = rs * 2 /* hex */ + rs / gs /* spaces */ + 1 /* space */ + len /* ascii */; | ||
163 | he = (gs * 2 /* hex */ + 1 /* space */) * len / gs - 1 /* no trailing space */; | ||
128 | 164 | ||
129 | if (ascii) | 165 | if (ascii) |
130 | e = 50; | 166 | e = ae; |
131 | else | 167 | else |
132 | e = 2; | 168 | e = he; |
133 | buf[e + 2] = '\0'; | 169 | |
134 | 170 | f = min_t(int, e + 1, buflen); | |
135 | if (!l) { | 171 | if (buflen) { |
136 | a = r == e && buf[0] == ' '; | 172 | test_hexdump_prepare_test(len, rs, gs, test, sizeof(test), ascii); |
137 | } else if (l < 3) { | 173 | test[f - 1] = '\0'; |
138 | a = r == e && buf[0] == '\0'; | ||
139 | } else if (l < 4) { | ||
140 | a = r == e && !strcmp(buf, t); | ||
141 | } else if (ascii) { | ||
142 | if (l < 51) | ||
143 | a = r == e && buf[l - 1] == '\0' && buf[l - 2] == ' '; | ||
144 | else | ||
145 | a = r == e && buf[50] == '\0' && buf[49] == '.'; | ||
146 | } else { | ||
147 | a = r == e && buf[e] == '\0'; | ||
148 | } | 174 | } |
175 | memset(test + f, FILL_CHAR, sizeof(test) - f); | ||
176 | |||
177 | a = r == e && !memcmp(test, buf, TEST_HEXDUMP_BUF_SIZE); | ||
178 | |||
179 | buf[sizeof(buf) - 1] = '\0'; | ||
149 | 180 | ||
150 | if (!a) { | 181 | if (!a) { |
151 | pr_err("Len: %zu rc: %u strlen: %zu\n", l, r, strlen(buf)); | 182 | pr_err("Len: %zu buflen: %zu strlen: %zu\n", |
152 | pr_err("Result: '%s'\n", buf); | 183 | len, buflen, strnlen(buf, sizeof(buf))); |
184 | pr_err("Result: %d '%s'\n", r, buf); | ||
185 | pr_err("Expect: %d '%s'\n", e, test); | ||
186 | failed_tests++; | ||
153 | } | 187 | } |
154 | } | 188 | } |
155 | 189 | ||
190 | static void __init test_hexdump_overflow_set(size_t buflen, bool ascii) | ||
191 | { | ||
192 | unsigned int i = 0; | ||
193 | int rs = (get_random_int() % 2 + 1) * 16; | ||
194 | |||
195 | do { | ||
196 | int gs = 1 << i; | ||
197 | size_t len = get_random_int() % rs + gs; | ||
198 | |||
199 | test_hexdump_overflow(buflen, rounddown(len, gs), rs, gs, ascii); | ||
200 | } while (i++ < 3); | ||
201 | } | ||
202 | |||
156 | static int __init test_hexdump_init(void) | 203 | static int __init test_hexdump_init(void) |
157 | { | 204 | { |
158 | unsigned int i; | 205 | unsigned int i; |
159 | int rowsize; | 206 | int rowsize; |
160 | 207 | ||
161 | pr_info("Running tests...\n"); | ||
162 | |||
163 | rowsize = (get_random_int() % 2 + 1) * 16; | 208 | rowsize = (get_random_int() % 2 + 1) * 16; |
164 | for (i = 0; i < 16; i++) | 209 | for (i = 0; i < 16; i++) |
165 | test_hexdump_set(rowsize, false); | 210 | test_hexdump_set(rowsize, false); |
@@ -168,13 +213,26 @@ static int __init test_hexdump_init(void) | |||
168 | for (i = 0; i < 16; i++) | 213 | for (i = 0; i < 16; i++) |
169 | test_hexdump_set(rowsize, true); | 214 | test_hexdump_set(rowsize, true); |
170 | 215 | ||
171 | for (i = 0; i < 16; i++) | 216 | for (i = 0; i <= TEST_HEXDUMP_BUF_SIZE; i++) |
172 | test_hexdump_overflow(false); | 217 | test_hexdump_overflow_set(i, false); |
173 | 218 | ||
174 | for (i = 0; i < 16; i++) | 219 | for (i = 0; i <= TEST_HEXDUMP_BUF_SIZE; i++) |
175 | test_hexdump_overflow(true); | 220 | test_hexdump_overflow_set(i, true); |
221 | |||
222 | if (failed_tests == 0) | ||
223 | pr_info("all %u tests passed\n", total_tests); | ||
224 | else | ||
225 | pr_err("failed %u out of %u tests\n", failed_tests, total_tests); | ||
176 | 226 | ||
177 | return -EINVAL; | 227 | return failed_tests ? -EINVAL : 0; |
178 | } | 228 | } |
179 | module_init(test_hexdump_init); | 229 | module_init(test_hexdump_init); |
230 | |||
231 | static void __exit test_hexdump_exit(void) | ||
232 | { | ||
233 | /* do nothing */ | ||
234 | } | ||
235 | module_exit(test_hexdump_exit); | ||
236 | |||
237 | MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); | ||
180 | MODULE_LICENSE("Dual BSD/GPL"); | 238 | MODULE_LICENSE("Dual BSD/GPL"); |
diff --git a/lib/ubsan.c b/lib/ubsan.c new file mode 100644 index 000000000000..8799ae5e2e42 --- /dev/null +++ b/lib/ubsan.c | |||
@@ -0,0 +1,456 @@ | |||
1 | /* | ||
2 | * UBSAN error reporting functions | ||
3 | * | ||
4 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
5 | * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/bitops.h> | ||
14 | #include <linux/bug.h> | ||
15 | #include <linux/ctype.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/sched.h> | ||
20 | |||
21 | #include "ubsan.h" | ||
22 | |||
23 | const char *type_check_kinds[] = { | ||
24 | "load of", | ||
25 | "store to", | ||
26 | "reference binding to", | ||
27 | "member access within", | ||
28 | "member call on", | ||
29 | "constructor call on", | ||
30 | "downcast of", | ||
31 | "downcast of" | ||
32 | }; | ||
33 | |||
34 | #define REPORTED_BIT 31 | ||
35 | |||
36 | #if (BITS_PER_LONG == 64) && defined(__BIG_ENDIAN) | ||
37 | #define COLUMN_MASK (~(1U << REPORTED_BIT)) | ||
38 | #define LINE_MASK (~0U) | ||
39 | #else | ||
40 | #define COLUMN_MASK (~0U) | ||
41 | #define LINE_MASK (~(1U << REPORTED_BIT)) | ||
42 | #endif | ||
43 | |||
44 | #define VALUE_LENGTH 40 | ||
45 | |||
46 | static bool was_reported(struct source_location *location) | ||
47 | { | ||
48 | return test_and_set_bit(REPORTED_BIT, &location->reported); | ||
49 | } | ||
50 | |||
51 | static void print_source_location(const char *prefix, | ||
52 | struct source_location *loc) | ||
53 | { | ||
54 | pr_err("%s %s:%d:%d\n", prefix, loc->file_name, | ||
55 | loc->line & LINE_MASK, loc->column & COLUMN_MASK); | ||
56 | } | ||
57 | |||
58 | static bool suppress_report(struct source_location *loc) | ||
59 | { | ||
60 | return current->in_ubsan || was_reported(loc); | ||
61 | } | ||
62 | |||
63 | static bool type_is_int(struct type_descriptor *type) | ||
64 | { | ||
65 | return type->type_kind == type_kind_int; | ||
66 | } | ||
67 | |||
68 | static bool type_is_signed(struct type_descriptor *type) | ||
69 | { | ||
70 | WARN_ON(!type_is_int(type)); | ||
71 | return type->type_info & 1; | ||
72 | } | ||
73 | |||
74 | static unsigned type_bit_width(struct type_descriptor *type) | ||
75 | { | ||
76 | return 1 << (type->type_info >> 1); | ||
77 | } | ||
78 | |||
79 | static bool is_inline_int(struct type_descriptor *type) | ||
80 | { | ||
81 | unsigned inline_bits = sizeof(unsigned long)*8; | ||
82 | unsigned bits = type_bit_width(type); | ||
83 | |||
84 | WARN_ON(!type_is_int(type)); | ||
85 | |||
86 | return bits <= inline_bits; | ||
87 | } | ||
88 | |||
89 | static s_max get_signed_val(struct type_descriptor *type, unsigned long val) | ||
90 | { | ||
91 | if (is_inline_int(type)) { | ||
92 | unsigned extra_bits = sizeof(s_max)*8 - type_bit_width(type); | ||
93 | return ((s_max)val) << extra_bits >> extra_bits; | ||
94 | } | ||
95 | |||
96 | if (type_bit_width(type) == 64) | ||
97 | return *(s64 *)val; | ||
98 | |||
99 | return *(s_max *)val; | ||
100 | } | ||
101 | |||
102 | static bool val_is_negative(struct type_descriptor *type, unsigned long val) | ||
103 | { | ||
104 | return type_is_signed(type) && get_signed_val(type, val) < 0; | ||
105 | } | ||
106 | |||
107 | static u_max get_unsigned_val(struct type_descriptor *type, unsigned long val) | ||
108 | { | ||
109 | if (is_inline_int(type)) | ||
110 | return val; | ||
111 | |||
112 | if (type_bit_width(type) == 64) | ||
113 | return *(u64 *)val; | ||
114 | |||
115 | return *(u_max *)val; | ||
116 | } | ||
117 | |||
118 | static void val_to_string(char *str, size_t size, struct type_descriptor *type, | ||
119 | unsigned long value) | ||
120 | { | ||
121 | if (type_is_int(type)) { | ||
122 | if (type_bit_width(type) == 128) { | ||
123 | #if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__) | ||
124 | u_max val = get_unsigned_val(type, value); | ||
125 | |||
126 | scnprintf(str, size, "0x%08x%08x%08x%08x", | ||
127 | (u32)(val >> 96), | ||
128 | (u32)(val >> 64), | ||
129 | (u32)(val >> 32), | ||
130 | (u32)(val)); | ||
131 | #else | ||
132 | WARN_ON(1); | ||
133 | #endif | ||
134 | } else if (type_is_signed(type)) { | ||
135 | scnprintf(str, size, "%lld", | ||
136 | (s64)get_signed_val(type, value)); | ||
137 | } else { | ||
138 | scnprintf(str, size, "%llu", | ||
139 | (u64)get_unsigned_val(type, value)); | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | static bool location_is_valid(struct source_location *loc) | ||
145 | { | ||
146 | return loc->file_name != NULL; | ||
147 | } | ||
148 | |||
149 | static DEFINE_SPINLOCK(report_lock); | ||
150 | |||
151 | static void ubsan_prologue(struct source_location *location, | ||
152 | unsigned long *flags) | ||
153 | { | ||
154 | current->in_ubsan++; | ||
155 | spin_lock_irqsave(&report_lock, *flags); | ||
156 | |||
157 | pr_err("========================================" | ||
158 | "========================================\n"); | ||
159 | print_source_location("UBSAN: Undefined behaviour in", location); | ||
160 | } | ||
161 | |||
162 | static void ubsan_epilogue(unsigned long *flags) | ||
163 | { | ||
164 | dump_stack(); | ||
165 | pr_err("========================================" | ||
166 | "========================================\n"); | ||
167 | spin_unlock_irqrestore(&report_lock, *flags); | ||
168 | current->in_ubsan--; | ||
169 | } | ||
170 | |||
171 | static void handle_overflow(struct overflow_data *data, unsigned long lhs, | ||
172 | unsigned long rhs, char op) | ||
173 | { | ||
174 | |||
175 | struct type_descriptor *type = data->type; | ||
176 | unsigned long flags; | ||
177 | char lhs_val_str[VALUE_LENGTH]; | ||
178 | char rhs_val_str[VALUE_LENGTH]; | ||
179 | |||
180 | if (suppress_report(&data->location)) | ||
181 | return; | ||
182 | |||
183 | ubsan_prologue(&data->location, &flags); | ||
184 | |||
185 | val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs); | ||
186 | val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs); | ||
187 | pr_err("%s integer overflow:\n", | ||
188 | type_is_signed(type) ? "signed" : "unsigned"); | ||
189 | pr_err("%s %c %s cannot be represented in type %s\n", | ||
190 | lhs_val_str, | ||
191 | op, | ||
192 | rhs_val_str, | ||
193 | type->type_name); | ||
194 | |||
195 | ubsan_epilogue(&flags); | ||
196 | } | ||
197 | |||
198 | void __ubsan_handle_add_overflow(struct overflow_data *data, | ||
199 | unsigned long lhs, | ||
200 | unsigned long rhs) | ||
201 | { | ||
202 | |||
203 | handle_overflow(data, lhs, rhs, '+'); | ||
204 | } | ||
205 | EXPORT_SYMBOL(__ubsan_handle_add_overflow); | ||
206 | |||
207 | void __ubsan_handle_sub_overflow(struct overflow_data *data, | ||
208 | unsigned long lhs, | ||
209 | unsigned long rhs) | ||
210 | { | ||
211 | handle_overflow(data, lhs, rhs, '-'); | ||
212 | } | ||
213 | EXPORT_SYMBOL(__ubsan_handle_sub_overflow); | ||
214 | |||
215 | void __ubsan_handle_mul_overflow(struct overflow_data *data, | ||
216 | unsigned long lhs, | ||
217 | unsigned long rhs) | ||
218 | { | ||
219 | handle_overflow(data, lhs, rhs, '*'); | ||
220 | } | ||
221 | EXPORT_SYMBOL(__ubsan_handle_mul_overflow); | ||
222 | |||
223 | void __ubsan_handle_negate_overflow(struct overflow_data *data, | ||
224 | unsigned long old_val) | ||
225 | { | ||
226 | unsigned long flags; | ||
227 | char old_val_str[VALUE_LENGTH]; | ||
228 | |||
229 | if (suppress_report(&data->location)) | ||
230 | return; | ||
231 | |||
232 | ubsan_prologue(&data->location, &flags); | ||
233 | |||
234 | val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val); | ||
235 | |||
236 | pr_err("negation of %s cannot be represented in type %s:\n", | ||
237 | old_val_str, data->type->type_name); | ||
238 | |||
239 | ubsan_epilogue(&flags); | ||
240 | } | ||
241 | EXPORT_SYMBOL(__ubsan_handle_negate_overflow); | ||
242 | |||
243 | |||
244 | void __ubsan_handle_divrem_overflow(struct overflow_data *data, | ||
245 | unsigned long lhs, | ||
246 | unsigned long rhs) | ||
247 | { | ||
248 | unsigned long flags; | ||
249 | char rhs_val_str[VALUE_LENGTH]; | ||
250 | |||
251 | if (suppress_report(&data->location)) | ||
252 | return; | ||
253 | |||
254 | ubsan_prologue(&data->location, &flags); | ||
255 | |||
256 | val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs); | ||
257 | |||
258 | if (type_is_signed(data->type) && get_signed_val(data->type, rhs) == -1) | ||
259 | pr_err("division of %s by -1 cannot be represented in type %s\n", | ||
260 | rhs_val_str, data->type->type_name); | ||
261 | else | ||
262 | pr_err("division by zero\n"); | ||
263 | |||
264 | ubsan_epilogue(&flags); | ||
265 | } | ||
266 | EXPORT_SYMBOL(__ubsan_handle_divrem_overflow); | ||
267 | |||
268 | static void handle_null_ptr_deref(struct type_mismatch_data *data) | ||
269 | { | ||
270 | unsigned long flags; | ||
271 | |||
272 | if (suppress_report(&data->location)) | ||
273 | return; | ||
274 | |||
275 | ubsan_prologue(&data->location, &flags); | ||
276 | |||
277 | pr_err("%s null pointer of type %s\n", | ||
278 | type_check_kinds[data->type_check_kind], | ||
279 | data->type->type_name); | ||
280 | |||
281 | ubsan_epilogue(&flags); | ||
282 | } | ||
283 | |||
284 | static void handle_missaligned_access(struct type_mismatch_data *data, | ||
285 | unsigned long ptr) | ||
286 | { | ||
287 | unsigned long flags; | ||
288 | |||
289 | if (suppress_report(&data->location)) | ||
290 | return; | ||
291 | |||
292 | ubsan_prologue(&data->location, &flags); | ||
293 | |||
294 | pr_err("%s misaligned address %p for type %s\n", | ||
295 | type_check_kinds[data->type_check_kind], | ||
296 | (void *)ptr, data->type->type_name); | ||
297 | pr_err("which requires %ld byte alignment\n", data->alignment); | ||
298 | |||
299 | ubsan_epilogue(&flags); | ||
300 | } | ||
301 | |||
302 | static void handle_object_size_mismatch(struct type_mismatch_data *data, | ||
303 | unsigned long ptr) | ||
304 | { | ||
305 | unsigned long flags; | ||
306 | |||
307 | if (suppress_report(&data->location)) | ||
308 | return; | ||
309 | |||
310 | ubsan_prologue(&data->location, &flags); | ||
311 | pr_err("%s address %pk with insufficient space\n", | ||
312 | type_check_kinds[data->type_check_kind], | ||
313 | (void *) ptr); | ||
314 | pr_err("for an object of type %s\n", data->type->type_name); | ||
315 | ubsan_epilogue(&flags); | ||
316 | } | ||
317 | |||
318 | void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, | ||
319 | unsigned long ptr) | ||
320 | { | ||
321 | |||
322 | if (!ptr) | ||
323 | handle_null_ptr_deref(data); | ||
324 | else if (data->alignment && !IS_ALIGNED(ptr, data->alignment)) | ||
325 | handle_missaligned_access(data, ptr); | ||
326 | else | ||
327 | handle_object_size_mismatch(data, ptr); | ||
328 | } | ||
329 | EXPORT_SYMBOL(__ubsan_handle_type_mismatch); | ||
330 | |||
331 | void __ubsan_handle_nonnull_return(struct nonnull_return_data *data) | ||
332 | { | ||
333 | unsigned long flags; | ||
334 | |||
335 | if (suppress_report(&data->location)) | ||
336 | return; | ||
337 | |||
338 | ubsan_prologue(&data->location, &flags); | ||
339 | |||
340 | pr_err("null pointer returned from function declared to never return null\n"); | ||
341 | |||
342 | if (location_is_valid(&data->attr_location)) | ||
343 | print_source_location("returns_nonnull attribute specified in", | ||
344 | &data->attr_location); | ||
345 | |||
346 | ubsan_epilogue(&flags); | ||
347 | } | ||
348 | EXPORT_SYMBOL(__ubsan_handle_nonnull_return); | ||
349 | |||
350 | void __ubsan_handle_vla_bound_not_positive(struct vla_bound_data *data, | ||
351 | unsigned long bound) | ||
352 | { | ||
353 | unsigned long flags; | ||
354 | char bound_str[VALUE_LENGTH]; | ||
355 | |||
356 | if (suppress_report(&data->location)) | ||
357 | return; | ||
358 | |||
359 | ubsan_prologue(&data->location, &flags); | ||
360 | |||
361 | val_to_string(bound_str, sizeof(bound_str), data->type, bound); | ||
362 | pr_err("variable length array bound value %s <= 0\n", bound_str); | ||
363 | |||
364 | ubsan_epilogue(&flags); | ||
365 | } | ||
366 | EXPORT_SYMBOL(__ubsan_handle_vla_bound_not_positive); | ||
367 | |||
368 | void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, | ||
369 | unsigned long index) | ||
370 | { | ||
371 | unsigned long flags; | ||
372 | char index_str[VALUE_LENGTH]; | ||
373 | |||
374 | if (suppress_report(&data->location)) | ||
375 | return; | ||
376 | |||
377 | ubsan_prologue(&data->location, &flags); | ||
378 | |||
379 | val_to_string(index_str, sizeof(index_str), data->index_type, index); | ||
380 | pr_err("index %s is out of range for type %s\n", index_str, | ||
381 | data->array_type->type_name); | ||
382 | ubsan_epilogue(&flags); | ||
383 | } | ||
384 | EXPORT_SYMBOL(__ubsan_handle_out_of_bounds); | ||
385 | |||
386 | void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data, | ||
387 | unsigned long lhs, unsigned long rhs) | ||
388 | { | ||
389 | unsigned long flags; | ||
390 | struct type_descriptor *rhs_type = data->rhs_type; | ||
391 | struct type_descriptor *lhs_type = data->lhs_type; | ||
392 | char rhs_str[VALUE_LENGTH]; | ||
393 | char lhs_str[VALUE_LENGTH]; | ||
394 | |||
395 | if (suppress_report(&data->location)) | ||
396 | return; | ||
397 | |||
398 | ubsan_prologue(&data->location, &flags); | ||
399 | |||
400 | val_to_string(rhs_str, sizeof(rhs_str), rhs_type, rhs); | ||
401 | val_to_string(lhs_str, sizeof(lhs_str), lhs_type, lhs); | ||
402 | |||
403 | if (val_is_negative(rhs_type, rhs)) | ||
404 | pr_err("shift exponent %s is negative\n", rhs_str); | ||
405 | |||
406 | else if (get_unsigned_val(rhs_type, rhs) >= | ||
407 | type_bit_width(lhs_type)) | ||
408 | pr_err("shift exponent %s is too large for %u-bit type %s\n", | ||
409 | rhs_str, | ||
410 | type_bit_width(lhs_type), | ||
411 | lhs_type->type_name); | ||
412 | else if (val_is_negative(lhs_type, lhs)) | ||
413 | pr_err("left shift of negative value %s\n", | ||
414 | lhs_str); | ||
415 | else | ||
416 | pr_err("left shift of %s by %s places cannot be" | ||
417 | " represented in type %s\n", | ||
418 | lhs_str, rhs_str, | ||
419 | lhs_type->type_name); | ||
420 | |||
421 | ubsan_epilogue(&flags); | ||
422 | } | ||
423 | EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds); | ||
424 | |||
425 | |||
426 | void __noreturn | ||
427 | __ubsan_handle_builtin_unreachable(struct unreachable_data *data) | ||
428 | { | ||
429 | unsigned long flags; | ||
430 | |||
431 | ubsan_prologue(&data->location, &flags); | ||
432 | pr_err("calling __builtin_unreachable()\n"); | ||
433 | ubsan_epilogue(&flags); | ||
434 | panic("can't return from __builtin_unreachable()"); | ||
435 | } | ||
436 | EXPORT_SYMBOL(__ubsan_handle_builtin_unreachable); | ||
437 | |||
438 | void __ubsan_handle_load_invalid_value(struct invalid_value_data *data, | ||
439 | unsigned long val) | ||
440 | { | ||
441 | unsigned long flags; | ||
442 | char val_str[VALUE_LENGTH]; | ||
443 | |||
444 | if (suppress_report(&data->location)) | ||
445 | return; | ||
446 | |||
447 | ubsan_prologue(&data->location, &flags); | ||
448 | |||
449 | val_to_string(val_str, sizeof(val_str), data->type, val); | ||
450 | |||
451 | pr_err("load of value %s is not a valid value for type %s\n", | ||
452 | val_str, data->type->type_name); | ||
453 | |||
454 | ubsan_epilogue(&flags); | ||
455 | } | ||
456 | EXPORT_SYMBOL(__ubsan_handle_load_invalid_value); | ||
diff --git a/lib/ubsan.h b/lib/ubsan.h new file mode 100644 index 000000000000..b2d18d4a53f5 --- /dev/null +++ b/lib/ubsan.h | |||
@@ -0,0 +1,84 @@ | |||
1 | #ifndef _LIB_UBSAN_H | ||
2 | #define _LIB_UBSAN_H | ||
3 | |||
4 | enum { | ||
5 | type_kind_int = 0, | ||
6 | type_kind_float = 1, | ||
7 | type_unknown = 0xffff | ||
8 | }; | ||
9 | |||
10 | struct type_descriptor { | ||
11 | u16 type_kind; | ||
12 | u16 type_info; | ||
13 | char type_name[1]; | ||
14 | }; | ||
15 | |||
16 | struct source_location { | ||
17 | const char *file_name; | ||
18 | union { | ||
19 | unsigned long reported; | ||
20 | struct { | ||
21 | u32 line; | ||
22 | u32 column; | ||
23 | }; | ||
24 | }; | ||
25 | }; | ||
26 | |||
27 | struct overflow_data { | ||
28 | struct source_location location; | ||
29 | struct type_descriptor *type; | ||
30 | }; | ||
31 | |||
32 | struct type_mismatch_data { | ||
33 | struct source_location location; | ||
34 | struct type_descriptor *type; | ||
35 | unsigned long alignment; | ||
36 | unsigned char type_check_kind; | ||
37 | }; | ||
38 | |||
39 | struct nonnull_arg_data { | ||
40 | struct source_location location; | ||
41 | struct source_location attr_location; | ||
42 | int arg_index; | ||
43 | }; | ||
44 | |||
45 | struct nonnull_return_data { | ||
46 | struct source_location location; | ||
47 | struct source_location attr_location; | ||
48 | }; | ||
49 | |||
50 | struct vla_bound_data { | ||
51 | struct source_location location; | ||
52 | struct type_descriptor *type; | ||
53 | }; | ||
54 | |||
55 | struct out_of_bounds_data { | ||
56 | struct source_location location; | ||
57 | struct type_descriptor *array_type; | ||
58 | struct type_descriptor *index_type; | ||
59 | }; | ||
60 | |||
61 | struct shift_out_of_bounds_data { | ||
62 | struct source_location location; | ||
63 | struct type_descriptor *lhs_type; | ||
64 | struct type_descriptor *rhs_type; | ||
65 | }; | ||
66 | |||
67 | struct unreachable_data { | ||
68 | struct source_location location; | ||
69 | }; | ||
70 | |||
71 | struct invalid_value_data { | ||
72 | struct source_location location; | ||
73 | struct type_descriptor *type; | ||
74 | }; | ||
75 | |||
76 | #if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__) | ||
77 | typedef __int128 s_max; | ||
78 | typedef unsigned __int128 u_max; | ||
79 | #else | ||
80 | typedef s64 s_max; | ||
81 | typedef u64 u_max; | ||
82 | #endif | ||
83 | |||
84 | #endif | ||