pcb-rnd knowledge pool

 

Crash: plugin uninit errors

plugin_unreg by Tibor 'Igor2' Palinkas on 2018-05-16

Tags: dev, plugin, uninit, crash, segfault

node source

 

 

Abstract: Strange looking bug: pcb-rnd crashes on exit when dynamic loaded plugins are used. Does not happen with the same version with buildins. The reason is usually the same common error: plugin leaving registered objects behind.

 

pcb-rnd has two main parts: core and plugins. Core runs some central infra, plugins implement various features that depend on the central infra. Some infra are used as "plugin registers something in code on plugin init and unregisters it on plugin uninit". Most often these registrations are tracked by const char * cookie, defined in the plugin. This way each cookie is unique and we need to do cheap pointer comparison only, but in a debugger or error message we can print the content of the cookie (which is a human readable desc) instead of a meaningless pointer.

That way the cookie is always placed in the text segment of the plugin - this will be important later.

If you write a plugin that registers a few things on init and forgets to unregister them on uninit, the plugin will leak. With static linked plugins (buildin) this doesn't matter, you leak something that will only live another 15 ms before the whole process dies anyway. But this will lead to more serious consequences with dynamic linking:

1. if you unload a plugin runtime, and it leaves some references in code infra, later on other parts of the code may try to evaluate these references but the plugin is already unloaded so this would lead to a crash.

2. You may say, this does not matter if you don't unload plugins on the fly, only at the end. But unfortunately it does there too, and will definitely crash pcb-rnd on uninit: the cookies are stored in the text segment of the plugin .so; when you unload plugin 'A' on uninit, you render its cookies unreadable. If it left some stuff registered with these cookies in central infra, when plugin 'B' is unregistering and the core tries to crawl the list of registered items, it may need to look behind some cookies; hitting the leftover from 'A', this will lead to dereferencing a page of the text segment of plugin 'A', which is already invalid. This will happen rarely as we won't need to read the cookie text unless we want to give an error message, but...

3. ... the same happens with any other non-cookie string or data that needs to be read. This is what happened in this specific case: because config node unregistering requires reading config paths, and config paths are const char * static strings in plugins, if a dynamic loaded plugin forgets to unregister a config path it registered on init, we have a config path in our config database that points to unreadable memory. When we want to check if we need to free this path for another plugin's unregister, while reading it, pcb-rnd segfaulted.

Solution

Not unregistering anything that got registered by the plugin is a bug, because we want to be able to cleanly unload any plugin any time and go on running pcb-rnd (or go on unloading other plugins and cleanly exit). So pcb-rnd has checks and drastic sounding error messages for the case if a plugin leaves anything behind. These checks won't work in dynamic loaded plugin mode (we crash before getting to these) but they perfectly work with static linking. And static linking is how all development is normally done, so it will be hard to miss such bugs.