ObjFW  Check-in [3b3729a316]

Overview
Comment:runtime: Don't use static selectors

Static selectors are initialized by a global constructor that might run
too late: If a class has a +[load] that triggers an +[initialize], this
could result in trying to call +[initialize] with the selector in
class.m still being uninitialized. When this happens, it results in a
weird looking crash in an entirely unrelated method. This is because
when uninitialized, the selector name has not been replaced with a
selector UID yet, meaning the pointer to the selector name is treated as
the selector UID. As the dispatch only considers the low 16 bits of the
selector UID for performance reasons, it will just take the low 16 bits
of the pointer to the selector name as the UID without any complaints.
This can then result in calling a random method on that class which then
crashes.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 3b3729a316710420baf0fc9c7b032a94225c91da7209214e62edf86d3c25a212
User & Date: js on 2019-06-25 20:09:55
Other Links: manifest | tags
Context
2019-06-25
20:53
Add OFASN1DERRepresentation protocol check-in: cbaae715ce user: js tags: trunk
20:09
runtime: Don't use static selectors check-in: 3b3729a316 user: js tags: trunk
2019-06-23
22:53
Add -[OFASN1BitString DEREncodedValue] check-in: ea6e0818b0 user: js tags: trunk
Changes

Modified src/runtime/class.m from [15508d0871] to [132bb904b5].

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167





168
169
170
171
172
173
174
175
176
177
178
179
180
181

	objc_global_mutex_unlock();

	return class;
}

static void
callMethod(Class class, const char *method)
{
	SEL selector = sel_registerName(method);

	for (struct objc_method_list *methodList = class->isa->methodList;
	    methodList != NULL; methodList = methodList->next)
		for (unsigned int i = 0; i < methodList->count; i++)
			if (sel_isEqual((SEL)&methodList->methods[i].selector,
			    selector))
				((void (*)(id, SEL))methodList->methods[i]
				    .implementation)(class, selector);
}

static bool
hasLoad(Class class)
{



	SEL selector = sel_registerName("load");

	for (struct objc_method_list *methodList = class->isa->methodList;
	    methodList != NULL; methodList = methodList->next)
		for (size_t i = 0; i < methodList->count; i++)
			if (sel_isEqual((SEL)&methodList->methods[i].selector,
			    selector))
				return true;

	return false;
}

static void
callLoad(Class class)
{





	if (class->info & OBJC_CLASS_INFO_LOADED)
		return;

	if (class->superclass != Nil)
		callLoad(class->superclass);

	callMethod(class, "load");

	class->info |= OBJC_CLASS_INFO_LOADED;
}

void
objc_update_dtable(Class class)
{







|

<
<












>
>
>
|





|








>
>
>
>
>






|







130
131
132
133
134
135
136
137
138


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

	objc_global_mutex_unlock();

	return class;
}

static void
callSelector(Class class, SEL selector)
{


	for (struct objc_method_list *methodList = class->isa->methodList;
	    methodList != NULL; methodList = methodList->next)
		for (unsigned int i = 0; i < methodList->count; i++)
			if (sel_isEqual((SEL)&methodList->methods[i].selector,
			    selector))
				((void (*)(id, SEL))methodList->methods[i]
				    .implementation)(class, selector);
}

static bool
hasLoad(Class class)
{
	static SEL loadSel = NULL;

	if (loadSel == NULL)
		loadSel = sel_registerName("load");

	for (struct objc_method_list *methodList = class->isa->methodList;
	    methodList != NULL; methodList = methodList->next)
		for (size_t i = 0; i < methodList->count; i++)
			if (sel_isEqual((SEL)&methodList->methods[i].selector,
			    loadSel))
				return true;

	return false;
}

static void
callLoad(Class class)
{
	static SEL loadSel = NULL;

	if (loadSel == NULL)
		loadSel = sel_registerName("load");

	if (class->info & OBJC_CLASS_INFO_LOADED)
		return;

	if (class->superclass != Nil)
		callLoad(class->superclass);

	callSelector(class, loadSel);

	class->info |= OBJC_CLASS_INFO_LOADED;
}

void
objc_update_dtable(Class class)
{
312
313
314
315
316
317
318





319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337





338

339

340

341
342
343
344
345
346
347
	class->info |= OBJC_CLASS_INFO_SETUP;
	class->isa->info |= OBJC_CLASS_INFO_SETUP;
}

static void
initializeClass(Class class)
{





	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;

	if (class->superclass)
		initializeClass(class->superclass);

	class->info |= OBJC_CLASS_INFO_DTABLE;
	class->isa->info |= OBJC_CLASS_INFO_DTABLE;

	objc_update_dtable(class);
	objc_update_dtable(class->isa);

	/*
	 * Set it first to prevent calling it recursively due to message sends
	 * in the initialize method
	 */
	class->info |= OBJC_CLASS_INFO_INITIALIZED;
	class->isa->info |= OBJC_CLASS_INFO_INITIALIZED;






	if (class_respondsToSelector(object_getClass(class),

	    @selector(initialize)))

		[class initialize];

}

void
objc_initialize_class(Class class)
{
	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;







>
>
>
>
>



















>
>
>
>
>
|
>
|
>
|
>







318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
	class->info |= OBJC_CLASS_INFO_SETUP;
	class->isa->info |= OBJC_CLASS_INFO_SETUP;
}

static void
initializeClass(Class class)
{
	static SEL initializeSel = NULL;

	if (initializeSel == NULL)
		initializeSel = sel_registerName("initialize");

	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;

	if (class->superclass)
		initializeClass(class->superclass);

	class->info |= OBJC_CLASS_INFO_DTABLE;
	class->isa->info |= OBJC_CLASS_INFO_DTABLE;

	objc_update_dtable(class);
	objc_update_dtable(class->isa);

	/*
	 * Set it first to prevent calling it recursively due to message sends
	 * in the initialize method
	 */
	class->info |= OBJC_CLASS_INFO_INITIALIZED;
	class->isa->info |= OBJC_CLASS_INFO_INITIALIZED;

	/*
	 * +[initialize] might get called from some +[load], before the
	 * constructors of this compilation module have been called, at which
	 * point the selector would not be properly initialized.
	 */
	if (class_respondsToSelector(object_getClass(class), initializeSel)) {
		void (*initialize)(id, SEL) = (void (*)(id, SEL))
		    objc_msg_lookup(class, initializeSel);

		initialize(class, initializeSel);
	}
}

void
objc_initialize_class(Class class)
{
	if (class->info & OBJC_CLASS_INFO_INITIALIZED)
		return;
888
889
890
891
892
893
894





895
896
897
898
899
900
901
902
903
904
905
906

	class->info &= ~OBJC_CLASS_INFO_SETUP;
}

void
objc_unregister_class(Class class)
{





	while (class->subclassList != NULL && class->subclassList[0] != Nil)
		objc_unregister_class(class->subclassList[0]);

	if (class->info & OBJC_CLASS_INFO_LOADED)
		callMethod(class, "unload");

	objc_hashtable_delete(classes, class->name);

	if (strcmp(class_getName(class), "Protocol") != 0)
		classesCount--;

	unregisterClass(class);







>
>
>
>
>




|







907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930

	class->info &= ~OBJC_CLASS_INFO_SETUP;
}

void
objc_unregister_class(Class class)
{
	static SEL unloadSel = NULL;

	if (unloadSel == NULL)
		unloadSel = sel_registerName("unload");

	while (class->subclassList != NULL && class->subclassList[0] != Nil)
		objc_unregister_class(class->subclassList[0]);

	if (class->info & OBJC_CLASS_INFO_LOADED)
		callSelector(class, unloadSel);

	objc_hashtable_delete(classes, class->name);

	if (strcmp(class_getName(class), "Protocol") != 0)
		classesCount--;

	unregisterClass(class);