Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
  3
  4#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  5
  6#include <linux/module.h>
  7#include <linux/kernel.h>
  8#include <linux/list.h>
  9#include <linux/livepatch.h>
 10#include <linux/slab.h>
 11
 12/*
 13 * Keep a small list of pointers so that we can print address-agnostic
 14 * pointer values.  Use a rolling integer count to differentiate the values.
 15 * Ironically we could have used the shadow variable API to do this, but
 16 * let's not lean too heavily on the very code we're testing.
 17 */
 18static LIST_HEAD(ptr_list);
 19struct shadow_ptr {
 20	void *ptr;
 21	int id;
 22	struct list_head list;
 23};
 24
 25static void free_ptr_list(void)
 26{
 27	struct shadow_ptr *sp, *tmp_sp;
 28
 29	list_for_each_entry_safe(sp, tmp_sp, &ptr_list, list) {
 30		list_del(&sp->list);
 31		kfree(sp);
 32	}
 33}
 34
 35static int ptr_id(void *ptr)
 36{
 37	struct shadow_ptr *sp;
 38	static int count;
 39
 40	list_for_each_entry(sp, &ptr_list, list) {
 41		if (sp->ptr == ptr)
 42			return sp->id;
 43	}
 44
 45	sp = kmalloc(sizeof(*sp), GFP_ATOMIC);
 46	if (!sp)
 47		return -ENOMEM;
 48	sp->ptr = ptr;
 49	sp->id = count++;
 50
 51	list_add(&sp->list, &ptr_list);
 52
 53	return sp->id;
 54}
 55
 56/*
 57 * Shadow variable wrapper functions that echo the function and arguments
 58 * to the kernel log for testing verification.  Don't display raw pointers,
 59 * but use the ptr_id() value instead.
 60 */
 61static void *shadow_get(void *obj, unsigned long id)
 62{
 63	void *ret = klp_shadow_get(obj, id);
 64
 65	pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n",
 66		__func__, ptr_id(obj), id, ptr_id(ret));
 67
 68	return ret;
 69}
 70
 71static void *shadow_alloc(void *obj, unsigned long id, size_t size,
 72			  gfp_t gfp_flags, klp_shadow_ctor_t ctor,
 73			  void *ctor_data)
 74{
 75	void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor,
 76				     ctor_data);
 77	pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n",
 78		__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
 79		ptr_id(ctor_data), ptr_id(ret));
 80	return ret;
 81}
 82
 83static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
 84				 gfp_t gfp_flags, klp_shadow_ctor_t ctor,
 85				 void *ctor_data)
 86{
 87	void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor,
 88					    ctor_data);
 89	pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n",
 90		__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
 91		ptr_id(ctor_data), ptr_id(ret));
 92	return ret;
 93}
 94
 95static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
 96{
 97	klp_shadow_free(obj, id, dtor);
 98	pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n",
 99		__func__, ptr_id(obj), id, ptr_id(dtor));
100}
101
102static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
103{
104	klp_shadow_free_all(id, dtor);
105	pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n",
106		__func__, id, ptr_id(dtor));
107}
108
109
110/* Shadow variable constructor - remember simple pointer data */
111static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
112{
113	int **shadow_int = shadow_data;
114	*shadow_int = ctor_data;
115	pr_info("%s: PTR%d -> PTR%d\n",
116		__func__, ptr_id(shadow_int), ptr_id(ctor_data));
117
118	return 0;
119}
120
121static void shadow_dtor(void *obj, void *shadow_data)
122{
123	pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n",
124		__func__, ptr_id(obj), ptr_id(shadow_data));
125}
126
127static int test_klp_shadow_vars_init(void)
128{
129	void *obj			= THIS_MODULE;
130	int id			= 0x1234;
131	size_t size		= sizeof(int *);
132	gfp_t gfp_flags		= GFP_KERNEL;
133
134	int var1, var2, var3, var4;
135	int **sv1, **sv2, **sv3, **sv4;
136
137	void *ret;
138
139	ptr_id(NULL);
140	ptr_id(&var1);
141	ptr_id(&var2);
142	ptr_id(&var3);
143	ptr_id(&var4);
144
145	/*
146	 * With an empty shadow variable hash table, expect not to find
147	 * any matches.
148	 */
149	ret = shadow_get(obj, id);
150	if (!ret)
151		pr_info("  got expected NULL result\n");
152
153	/*
154	 * Allocate a few shadow variables with different <obj> and <id>.
155	 */
156	sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
157	if (!sv1)
158		return -ENOMEM;
159
160	sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
161	if (!sv2)
162		return -ENOMEM;
163
164	sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
165	if (!sv3)
166		return -ENOMEM;
167
168	/*
169	 * Verify we can find our new shadow variables and that they point
170	 * to expected data.
171	 */
172	ret = shadow_get(obj, id);
173	if (!ret)
174		return -EINVAL;
175	if (ret == sv1 && *sv1 == &var1)
176		pr_info("  got expected PTR%d -> PTR%d result\n",
177			ptr_id(sv1), ptr_id(*sv1));
178
179	ret = shadow_get(obj + 1, id);
180	if (!ret)
181		return -EINVAL;
182	if (ret == sv2 && *sv2 == &var2)
183		pr_info("  got expected PTR%d -> PTR%d result\n",
184			ptr_id(sv2), ptr_id(*sv2));
185	ret = shadow_get(obj, id + 1);
186	if (!ret)
187		return -EINVAL;
188	if (ret == sv3 && *sv3 == &var3)
189		pr_info("  got expected PTR%d -> PTR%d result\n",
190			ptr_id(sv3), ptr_id(*sv3));
191
192	/*
193	 * Allocate or get a few more, this time with the same <obj>, <id>.
194	 * The second invocation should return the same shadow var.
195	 */
196	sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
197	if (!sv4)
198		return -ENOMEM;
199
200	ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
201	if (!ret)
202		return -EINVAL;
203	if (ret == sv4 && *sv4 == &var4)
204		pr_info("  got expected PTR%d -> PTR%d result\n",
205			ptr_id(sv4), ptr_id(*sv4));
206
207	/*
208	 * Free the <obj=*, id> shadow variables and check that we can no
209	 * longer find them.
210	 */
211	shadow_free(obj, id, shadow_dtor);			/* sv1 */
212	ret = shadow_get(obj, id);
213	if (!ret)
214		pr_info("  got expected NULL result\n");
215
216	shadow_free(obj + 1, id, shadow_dtor);			/* sv2 */
217	ret = shadow_get(obj + 1, id);
218	if (!ret)
219		pr_info("  got expected NULL result\n");
220
221	shadow_free(obj + 2, id, shadow_dtor);			/* sv4 */
222	ret = shadow_get(obj + 2, id);
223	if (!ret)
224		pr_info("  got expected NULL result\n");
225
226	/*
227	 * We should still find an <id+1> variable.
228	 */
229	ret = shadow_get(obj, id + 1);
230	if (!ret)
231		return -EINVAL;
232	if (ret == sv3 && *sv3 == &var3)
233		pr_info("  got expected PTR%d -> PTR%d result\n",
234			ptr_id(sv3), ptr_id(*sv3));
235
236	/*
237	 * Free all the <id+1> variables, too.
238	 */
239	shadow_free_all(id + 1, shadow_dtor);			/* sv3 */
240	ret = shadow_get(obj, id);
241	if (!ret)
242		pr_info("  shadow_get() got expected NULL result\n");
243
244
245	free_ptr_list();
246
247	return 0;
248}
249
250static void test_klp_shadow_vars_exit(void)
251{
252}
253
254module_init(test_klp_shadow_vars_init);
255module_exit(test_klp_shadow_vars_exit);
256MODULE_LICENSE("GPL");
257MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
258MODULE_DESCRIPTION("Livepatch test: shadow variables");