diff options
author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2021-03-19 14:58:36 -0400 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-03-20 18:54:57 +0000 |
commit | 0ffb5a8a49ba2e0438f46d1083f7a58d088bc153 (patch) | |
tree | 9faaf30bf7817befc1d17a0d97c2714af4129469 /meta | |
parent | b020c230c9ddfaa2901fa07df70e4854677e19a7 (diff) | |
download | poky-0ffb5a8a49ba2e0438f46d1083f7a58d088bc153.tar.gz |
lttng-modules: backport patches to fix build against 5.12+ kernel
There are four changes in addition to the 2.12.5 release that we
need to build against the 5.12 kernel. Rather than only rely on
people knowing to use devupstream support to build against newer
kernels, we backport the 4 patches while waiting for release.
(From OE-Core rev: 2d45c09bfbad969549c719654f72714324299f00)
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
5 files changed, 353 insertions, 0 deletions
diff --git a/meta/recipes-kernel/lttng/lttng-modules/0001-Fix-memory-leaks-on-event-destroy.patch b/meta/recipes-kernel/lttng/lttng-modules/0001-Fix-memory-leaks-on-event-destroy.patch new file mode 100644 index 0000000000..21da932a75 --- /dev/null +++ b/meta/recipes-kernel/lttng/lttng-modules/0001-Fix-memory-leaks-on-event-destroy.patch | |||
@@ -0,0 +1,58 @@ | |||
1 | From b3fdf78b15beb940918da1e41eb68e24ba31bb87 Mon Sep 17 00:00:00 2001 | ||
2 | From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||
3 | Date: Wed, 3 Mar 2021 10:10:16 -0500 | ||
4 | Subject: [PATCH 1/4] Fix: memory leaks on event destroy | ||
5 | |||
6 | Both filter runtime and event enabler ref objects are owned by the | ||
7 | event, but are not freed upon destruction of the event object, thus | ||
8 | leaking memory. | ||
9 | |||
10 | Upstream-status: backport | ||
11 | |||
12 | Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||
13 | Change-Id: Ice9b1c18b47584838aea2b965494d3c8391f4c84 | ||
14 | --- | ||
15 | lttng-events.c | 7 +++++++ | ||
16 | lttng-events.h | 1 + | ||
17 | 2 files changed, 8 insertions(+) | ||
18 | |||
19 | diff --git a/lttng-events.c b/lttng-events.c | ||
20 | index f3398adc..984bd341 100644 | ||
21 | --- a/lttng-events.c | ||
22 | +++ b/lttng-events.c | ||
23 | @@ -919,6 +919,8 @@ int _lttng_event_unregister(struct lttng_event *event) | ||
24 | static | ||
25 | void _lttng_event_destroy(struct lttng_event *event) | ||
26 | { | ||
27 | + struct lttng_enabler_ref *enabler_ref, *tmp_enabler_ref; | ||
28 | + | ||
29 | switch (event->instrumentation) { | ||
30 | case LTTNG_KERNEL_TRACEPOINT: | ||
31 | lttng_event_put(event->desc); | ||
32 | @@ -944,6 +946,11 @@ void _lttng_event_destroy(struct lttng_event *event) | ||
33 | } | ||
34 | list_del(&event->list); | ||
35 | lttng_destroy_context(event->ctx); | ||
36 | + lttng_free_event_filter_runtime(event); | ||
37 | + /* Free event enabler refs */ | ||
38 | + list_for_each_entry_safe(enabler_ref, tmp_enabler_ref, | ||
39 | + &event->enablers_ref_head, node) | ||
40 | + kfree(enabler_ref); | ||
41 | kmem_cache_free(event_cache, event); | ||
42 | } | ||
43 | |||
44 | diff --git a/lttng-events.h b/lttng-events.h | ||
45 | index 1b9ab167..13b6abf5 100644 | ||
46 | --- a/lttng-events.h | ||
47 | +++ b/lttng-events.h | ||
48 | @@ -716,6 +716,7 @@ int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler, | ||
49 | struct lttng_kernel_filter_bytecode __user *bytecode); | ||
50 | void lttng_enabler_event_link_bytecode(struct lttng_event *event, | ||
51 | struct lttng_enabler *enabler); | ||
52 | +void lttng_free_event_filter_runtime(struct lttng_event *event); | ||
53 | |||
54 | int lttng_probes_init(void); | ||
55 | |||
56 | -- | ||
57 | 2.19.1 | ||
58 | |||
diff --git a/meta/recipes-kernel/lttng/lttng-modules/0002-Fix-filter-interpreter-early-exits-on-uninitialized-.patch b/meta/recipes-kernel/lttng/lttng-modules/0002-Fix-filter-interpreter-early-exits-on-uninitialized-.patch new file mode 100644 index 0000000000..609690f05c --- /dev/null +++ b/meta/recipes-kernel/lttng/lttng-modules/0002-Fix-filter-interpreter-early-exits-on-uninitialized-.patch | |||
@@ -0,0 +1,159 @@ | |||
1 | From 23a2f61ffc6a656f136fa2044c0c3b8f79766779 Mon Sep 17 00:00:00 2001 | ||
2 | From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Galarneau?= | ||
3 | <jeremie.galarneau@efficios.com> | ||
4 | Date: Wed, 3 Mar 2021 18:52:19 -0500 | ||
5 | Subject: [PATCH 2/4] Fix: filter interpreter early-exits on uninitialized | ||
6 | value | ||
7 | MIME-Version: 1.0 | ||
8 | Content-Type: text/plain; charset=UTF-8 | ||
9 | Content-Transfer-Encoding: 8bit | ||
10 | |||
11 | I observed that syscall filtering on string arguments wouldn't work on | ||
12 | my development machines, both running 5.11.2-arch1-1 (Arch Linux). | ||
13 | |||
14 | For instance, enabling the tracing of the `openat()` syscall with the | ||
15 | 'filename == "/proc/cpuinfo"' filter would not produce events even | ||
16 | though matching events were present in another session that had no | ||
17 | filtering active. The same problem occurred with `execve()`. | ||
18 | |||
19 | I tried a couple of kernel versions before (5.11.1 and 5.10.13, if | ||
20 | memory serves me well) and I had the same problem. Meanwhile, I couldn't | ||
21 | reproduce the problem on various Debian machines (the LTTng CI) nor on a | ||
22 | fresh Ubuntu 20.04 with both the stock kernel and with an updated 5.11.2 | ||
23 | kernel. | ||
24 | |||
25 | I built the lttng-modules with the interpreter debugging printout and | ||
26 | saw the following warning: | ||
27 | LTTng: [debug bytecode in /home/jgalar/EfficiOS/src/lttng-modules/src/lttng-bytecode-interpreter.c:bytecode_interpret@1508] Bytecode warning: loading a NULL string. | ||
28 | |||
29 | After a shedload (yes, a _shed_load) of digging, I figured that the | ||
30 | problem was hidden in plain sight near that logging statement. | ||
31 | |||
32 | In the `BYTECODE_OP_LOAD_FIELD_REF_USER_STRING` operation, the 'ax' | ||
33 | register's 'user_str' is initialized with the stack value (the user | ||
34 | space string's address in our case). However, a NULL check is performed | ||
35 | against the register's 'str' member. | ||
36 | |||
37 | I initialy suspected that both members would be part of the same union | ||
38 | and alias each-other, but they are actually contiguous in a structure. | ||
39 | |||
40 | On the unaffected machines, I could confirm that the `str` member was | ||
41 | uninitialized to a non-zero value causing the condition to evaluate to | ||
42 | false. | ||
43 | |||
44 | Francis Deslauriers reproduced the problem by initializing the | ||
45 | interpreter stack to zero. | ||
46 | |||
47 | I am unsure of the exact kernel configuration option that reveals this | ||
48 | issue on Arch Linux, but my kernel has the following option enabled: | ||
49 | |||
50 | CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL: | ||
51 | Zero-initialize any stack variables that may be passed by reference | ||
52 | and had not already been explicitly initialized. This is intended to | ||
53 | eliminate all classes of uninitialized stack variable exploits and | ||
54 | information exposures. | ||
55 | |||
56 | I have not tried to build without this enabled as, anyhow, this seems | ||
57 | to be a legitimate issue. | ||
58 | |||
59 | I have spotted what appears to be an identical problem in | ||
60 | `BYTECODE_OP_LOAD_FIELD_REF_USER_SEQUENCE` and corrected it. However, | ||
61 | I have not exercised that code path. | ||
62 | |||
63 | The commit that introduced this problem is 5b4ad89. | ||
64 | |||
65 | The debug print-out of the `BYTECODE_OP_LOAD_FIELD_REF_USER_STRING` | ||
66 | operation is modified to print the user string (truncated to 31 chars). | ||
67 | |||
68 | Upstream-status: backport | ||
69 | |||
70 | Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com> | ||
71 | Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||
72 | Change-Id: I2da3c31b9e3ce0e1b164cf3d2711c0893cbec273 | ||
73 | --- | ||
74 | lttng-filter-interpreter.c | 41 ++++++++++++++++++++++++++++++++++---- | ||
75 | 1 file changed, 37 insertions(+), 4 deletions(-) | ||
76 | |||
77 | diff --git a/lttng-filter-interpreter.c b/lttng-filter-interpreter.c | ||
78 | index 5d572437..6e5a5139 100644 | ||
79 | --- a/lttng-filter-interpreter.c | ||
80 | +++ b/lttng-filter-interpreter.c | ||
81 | @@ -22,7 +22,7 @@ LTTNG_STACK_FRAME_NON_STANDARD(lttng_filter_interpret_bytecode); | ||
82 | * to handle user-space read. | ||
83 | */ | ||
84 | static | ||
85 | -char get_char(struct estack_entry *reg, size_t offset) | ||
86 | +char get_char(const struct estack_entry *reg, size_t offset) | ||
87 | { | ||
88 | if (unlikely(offset >= reg->u.s.seq_len)) | ||
89 | return '\0'; | ||
90 | @@ -593,6 +593,39 @@ end: | ||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | +#ifdef DEBUG | ||
95 | + | ||
96 | +#define DBG_USER_STR_CUTOFF 32 | ||
97 | + | ||
98 | +/* | ||
99 | + * In debug mode, print user string (truncated, if necessary). | ||
100 | + */ | ||
101 | +static inline | ||
102 | +void dbg_load_ref_user_str_printk(const struct estack_entry *user_str_reg) | ||
103 | +{ | ||
104 | + size_t pos = 0; | ||
105 | + char last_char; | ||
106 | + char user_str[DBG_USER_STR_CUTOFF]; | ||
107 | + | ||
108 | + pagefault_disable(); | ||
109 | + do { | ||
110 | + last_char = get_char(user_str_reg, pos); | ||
111 | + user_str[pos] = last_char; | ||
112 | + pos++; | ||
113 | + } while (last_char != '\0' && pos < sizeof(user_str)); | ||
114 | + pagefault_enable(); | ||
115 | + | ||
116 | + user_str[sizeof(user_str) - 1] = '\0'; | ||
117 | + dbg_printk("load field ref user string: '%s%s'\n", user_str, | ||
118 | + last_char != '\0' ? "[...]" : ""); | ||
119 | +} | ||
120 | +#else | ||
121 | +static inline | ||
122 | +void dbg_load_ref_user_str_printk(const struct estack_entry *user_str_reg) | ||
123 | +{ | ||
124 | +} | ||
125 | +#endif | ||
126 | + | ||
127 | /* | ||
128 | * Return 0 (discard), or raise the 0x1 flag (log event). | ||
129 | * Currently, other flags are kept for future extensions and have no | ||
130 | @@ -1313,7 +1346,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, | ||
131 | estack_push(stack, top, ax, bx); | ||
132 | estack_ax(stack, top)->u.s.user_str = | ||
133 | *(const char * const *) &filter_stack_data[ref->offset]; | ||
134 | - if (unlikely(!estack_ax(stack, top)->u.s.str)) { | ||
135 | + if (unlikely(!estack_ax(stack, top)->u.s.user_str)) { | ||
136 | dbg_printk("Filter warning: loading a NULL string.\n"); | ||
137 | ret = -EINVAL; | ||
138 | goto end; | ||
139 | @@ -1322,7 +1355,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, | ||
140 | estack_ax(stack, top)->u.s.literal_type = | ||
141 | ESTACK_STRING_LITERAL_TYPE_NONE; | ||
142 | estack_ax(stack, top)->u.s.user = 1; | ||
143 | - dbg_printk("ref load string %s\n", estack_ax(stack, top)->u.s.str); | ||
144 | + dbg_load_ref_user_str_printk(estack_ax(stack, top)); | ||
145 | next_pc += sizeof(struct load_op) + sizeof(struct field_ref); | ||
146 | PO; | ||
147 | } | ||
148 | @@ -1340,7 +1373,7 @@ uint64_t lttng_filter_interpret_bytecode(void *filter_data, | ||
149 | estack_ax(stack, top)->u.s.user_str = | ||
150 | *(const char **) (&filter_stack_data[ref->offset | ||
151 | + sizeof(unsigned long)]); | ||
152 | - if (unlikely(!estack_ax(stack, top)->u.s.str)) { | ||
153 | + if (unlikely(!estack_ax(stack, top)->u.s.user_str)) { | ||
154 | dbg_printk("Filter warning: loading a NULL sequence.\n"); | ||
155 | ret = -EINVAL; | ||
156 | goto end; | ||
157 | -- | ||
158 | 2.19.1 | ||
159 | |||
diff --git a/meta/recipes-kernel/lttng/lttng-modules/0003-fix-mm-tracing-record-slab-name-for-kmem_cache_free-.patch b/meta/recipes-kernel/lttng/lttng-modules/0003-fix-mm-tracing-record-slab-name-for-kmem_cache_free-.patch new file mode 100644 index 0000000000..71f99b80a3 --- /dev/null +++ b/meta/recipes-kernel/lttng/lttng-modules/0003-fix-mm-tracing-record-slab-name-for-kmem_cache_free-.patch | |||
@@ -0,0 +1,91 @@ | |||
1 | From 49c603ef2dc6969f4454f0d849af00ee24bb7f04 Mon Sep 17 00:00:00 2001 | ||
2 | From: Michael Jeanson <mjeanson@efficios.com> | ||
3 | Date: Thu, 4 Mar 2021 16:50:12 -0500 | ||
4 | Subject: [PATCH 3/4] fix: mm, tracing: record slab name for kmem_cache_free() | ||
5 | (v5.12) | ||
6 | |||
7 | See upstream commit: | ||
8 | |||
9 | commit 3544de8ee6e4817278b15fe08658de49abf58954 | ||
10 | Author: Jacob Wen <jian.w.wen@oracle.com> | ||
11 | Date: Wed Feb 24 12:00:55 2021 -0800 | ||
12 | |||
13 | mm, tracing: record slab name for kmem_cache_free() | ||
14 | |||
15 | Currently, a trace record generated by the RCU core is as below. | ||
16 | |||
17 | ... kmem_cache_free: call_site=rcu_core+0x1fd/0x610 ptr=00000000f3b49a66 | ||
18 | |||
19 | It doesn't tell us what the RCU core has freed. | ||
20 | |||
21 | This patch adds the slab name to trace_kmem_cache_free(). | ||
22 | The new format is as follows. | ||
23 | |||
24 | ... kmem_cache_free: call_site=rcu_core+0x1fd/0x610 ptr=0000000037f79c8d name=dentry | ||
25 | ... kmem_cache_free: call_site=rcu_core+0x1fd/0x610 ptr=00000000f78cb7b5 name=sock_inode_cache | ||
26 | ... kmem_cache_free: call_site=rcu_core+0x1fd/0x610 ptr=0000000018768985 name=pool_workqueue | ||
27 | ... kmem_cache_free: call_site=rcu_core+0x1fd/0x610 ptr=000000006a6cb484 name=radix_tree_node | ||
28 | |||
29 | We can use it to understand what the RCU core is going to free. For | ||
30 | example, some users maybe interested in when the RCU core starts | ||
31 | freeing reclaimable slabs like dentry to reduce memory pressure. | ||
32 | |||
33 | Link: https://lkml.kernel.org/r/20201216072804.8838-1-jian.w.wen@oracle.com | ||
34 | |||
35 | Upstream-status: backport | ||
36 | |||
37 | Signed-off-by: Michael Jeanson <mjeanson@efficios.com> | ||
38 | Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||
39 | Change-Id: I1ee2fc476614cadcc8d3ac5d8feddc7910e1aa3a | ||
40 | --- | ||
41 | instrumentation/events/lttng-module/kmem.h | 27 ++++++++++++++++++++++ | ||
42 | 1 file changed, 27 insertions(+) | ||
43 | |||
44 | diff --git a/instrumentation/events/lttng-module/kmem.h b/instrumentation/events/lttng-module/kmem.h | ||
45 | index b134620a..d787ea54 100644 | ||
46 | --- a/instrumentation/events/lttng-module/kmem.h | ||
47 | +++ b/instrumentation/events/lttng-module/kmem.h | ||
48 | @@ -87,6 +87,32 @@ LTTNG_TRACEPOINT_EVENT_INSTANCE(kmem_alloc_node, kmem_cache_alloc_node, | ||
49 | TP_ARGS(call_site, ptr, bytes_req, bytes_alloc, gfp_flags, node) | ||
50 | ) | ||
51 | |||
52 | +#if (LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(5,12,0)) | ||
53 | +LTTNG_TRACEPOINT_EVENT(kfree, | ||
54 | + | ||
55 | + TP_PROTO(unsigned long call_site, const void *ptr), | ||
56 | + | ||
57 | + TP_ARGS(call_site, ptr), | ||
58 | + | ||
59 | + TP_FIELDS( | ||
60 | + ctf_integer_hex(unsigned long, call_site, call_site) | ||
61 | + ctf_integer_hex(const void *, ptr, ptr) | ||
62 | + ) | ||
63 | +) | ||
64 | + | ||
65 | +LTTNG_TRACEPOINT_EVENT(kmem_cache_free, | ||
66 | + | ||
67 | + TP_PROTO(unsigned long call_site, const void *ptr, const char *name), | ||
68 | + | ||
69 | + TP_ARGS(call_site, ptr, name), | ||
70 | + | ||
71 | + TP_FIELDS( | ||
72 | + ctf_integer_hex(unsigned long, call_site, call_site) | ||
73 | + ctf_integer_hex(const void *, ptr, ptr) | ||
74 | + ctf_string(name, name) | ||
75 | + ) | ||
76 | +) | ||
77 | +#else | ||
78 | LTTNG_TRACEPOINT_EVENT_CLASS(kmem_free, | ||
79 | |||
80 | TP_PROTO(unsigned long call_site, const void *ptr), | ||
81 | @@ -114,6 +140,7 @@ LTTNG_TRACEPOINT_EVENT_INSTANCE(kmem_free, kmem_cache_free, | ||
82 | |||
83 | TP_ARGS(call_site, ptr) | ||
84 | ) | ||
85 | +#endif | ||
86 | |||
87 | #if (LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(3,3,0)) | ||
88 | LTTNG_TRACEPOINT_EVENT_MAP(mm_page_free, kmem_mm_page_free, | ||
89 | -- | ||
90 | 2.19.1 | ||
91 | |||
diff --git a/meta/recipes-kernel/lttng/lttng-modules/0004-Fix-kretprobe-null-ptr-deref-on-session-destroy.patch b/meta/recipes-kernel/lttng/lttng-modules/0004-Fix-kretprobe-null-ptr-deref-on-session-destroy.patch new file mode 100644 index 0000000000..8a839c2b43 --- /dev/null +++ b/meta/recipes-kernel/lttng/lttng-modules/0004-Fix-kretprobe-null-ptr-deref-on-session-destroy.patch | |||
@@ -0,0 +1,41 @@ | |||
1 | From 92cc3e7f76a545a2cd4828576971f1eea83f4e68 Mon Sep 17 00:00:00 2001 | ||
2 | From: Francis Deslauriers <francis.deslauriers@efficios.com> | ||
3 | Date: Wed, 17 Mar 2021 10:40:56 -0400 | ||
4 | Subject: [PATCH 4/4] Fix: kretprobe: null ptr deref on session destroy | ||
5 | |||
6 | The `filter_bytecode_runtime_head` list is currently not initialized for | ||
7 | the return event of the kretprobe. This caused a kernel null ptr | ||
8 | dereference when destroying a session. It can reproduced with the | ||
9 | following commands: | ||
10 | |||
11 | lttng create | ||
12 | lttng enable-event -k --function=lttng_test_filter_event_write my_event | ||
13 | lttng start | ||
14 | lttng stop | ||
15 | lttng destroy | ||
16 | |||
17 | Upstream-status: backport | ||
18 | |||
19 | Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com> | ||
20 | Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | ||
21 | Change-Id: I1162ce8b10dd7237a26331531f048346b984eee7 | ||
22 | --- | ||
23 | lttng-events.c | 2 ++ | ||
24 | 1 file changed, 2 insertions(+) | ||
25 | |||
26 | diff --git a/lttng-events.c b/lttng-events.c | ||
27 | index 984bd341..3450fa40 100644 | ||
28 | --- a/lttng-events.c | ||
29 | +++ b/lttng-events.c | ||
30 | @@ -704,6 +704,8 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, | ||
31 | event_return->enabled = 0; | ||
32 | event_return->registered = 1; | ||
33 | event_return->instrumentation = itype; | ||
34 | + INIT_LIST_HEAD(&event_return->bytecode_runtime_head); | ||
35 | + INIT_LIST_HEAD(&event_return->enablers_ref_head); | ||
36 | /* | ||
37 | * Populate lttng_event structure before kretprobe registration. | ||
38 | */ | ||
39 | -- | ||
40 | 2.19.1 | ||
41 | |||
diff --git a/meta/recipes-kernel/lttng/lttng-modules_2.12.5.bb b/meta/recipes-kernel/lttng/lttng-modules_2.12.5.bb index ea36a3464b..5b05c644a6 100644 --- a/meta/recipes-kernel/lttng/lttng-modules_2.12.5.bb +++ b/meta/recipes-kernel/lttng/lttng-modules_2.12.5.bb | |||
@@ -11,6 +11,10 @@ include lttng-platforms.inc | |||
11 | 11 | ||
12 | SRC_URI = "https://lttng.org/files/${BPN}/${BPN}-${PV}.tar.bz2 \ | 12 | SRC_URI = "https://lttng.org/files/${BPN}/${BPN}-${PV}.tar.bz2 \ |
13 | file://Makefile-Do-not-fail-if-CONFIG_TRACEPOINTS-is-not-en.patch \ | 13 | file://Makefile-Do-not-fail-if-CONFIG_TRACEPOINTS-is-not-en.patch \ |
14 | file://0001-Fix-memory-leaks-on-event-destroy.patch \ | ||
15 | file://0002-Fix-filter-interpreter-early-exits-on-uninitialized-.patch \ | ||
16 | file://0003-fix-mm-tracing-record-slab-name-for-kmem_cache_free-.patch \ | ||
17 | file://0004-Fix-kretprobe-null-ptr-deref-on-session-destroy.patch \ | ||
14 | " | 18 | " |
15 | 19 | ||
16 | SRC_URI[sha256sum] = "c4d1a1b42c728e37b6b7947ae16563a011c4b297311aa04d56f9a1791fb5a30a" | 20 | SRC_URI[sha256sum] = "c4d1a1b42c728e37b6b7947ae16563a011c4b297311aa04d56f9a1791fb5a30a" |