EnglishУкраїнськаmRussian
Login/New
Topic with no new replies

[BugWrong] Дедлоки в модулях VCAEngine, UserProtocol, WebUser


Author Message
Written on: 01. 05. 2023 [21:01]
dudanov
Sergey Dudanov
Topic creator
registered since: 14.08.2013
Posts: 26
Здравствуйте, Роман.

Снова выявились проблемы благодаря сборке системы под musl. На сей раз дедлоки в модулях VCAEngine, UserProtocol, WebUser. В крайних 2-х модулях дедлок возникал при загрузке системы, в первом - при обращении к библиотечному шаблону проекта tmplSO. Общая для всех случаев ситуация: один поток сначала захватывает мьютекс на запись (эксклюзивно), потом пытается захватить на чтение, владея правами на запись. Стандартом POSIX разрешается для rwlock многократный захват на чтение при условии эквивалентного количества освобождения, но явно запрещается потоку-владельцу на запись вызов захвата на чтение, поведение после вызова неопределено.

Ниже приведу стек вызовов всех 3-х случаев.

VCAEngine: Вызов stlPropSet производит захват на запись, а вызов stlSize следом пытается получить права на чтение, что приводит к дедлоку:

JAVASCRIPT
Thread 6 (LWP 50):
#0  __syscall_cp_c (nr=202, u=139988244794992, v=128, w=-1, x=0, y=0, z=0) at ./arch/x86_64/syscall_arch.h:61
#1  0x00007f51904b1ba9 in __futex4_cp (to=0x0, val=-1, op=128, addr=0x7f518d9a8e70) at src/thread/__timedwait.c:24
#2  __timedwait_cp (addr=addr@entry=0x7f518d9a8e70, val=val@entry=-1, clk=clk@entry=0, at=at@entry=0x0, priv=priv@entry=128) at src/thread/__timedwait.c:52
#3  0x00007f51904b1c4e in __timedwait (addr=addr@entry=0x7f518d9a8e70, val=-1, clk=clk@entry=0, at=at@entry=0x0, priv=128) at src/thread/__timedwait.c:68
#4  0x00007f51904b440c in __pthread_rwlock_timedrdlock (at=<optimized out>, rw=<optimized out>) at src/thread/pthread_rwlock_timedrdlock.c:18
#5  __pthread_rwlock_timedrdlock (rw=0x7f518d9a8e70, at=0x0) at src/thread/pthread_rwlock_timedrdlock.c:3
#6  0x00007f51900ce9c4 in OSCADA::ResRW::resRequestR (this=0x7f518d9a8e70, tm=0) at resalloc.cpp:86
#7  0x00007f51900cef94 in OSCADA::ResAlloc::request (this=0x7f518d806040, write=false, tm=0) at resalloc.cpp:132
#8  0x00007f51900ceefc in OSCADA::ResAlloc::ResAlloc (this=0x7f518d806040, rid=..., write=false, tm=0) at resalloc.cpp:122
#9  0x00007f518f1a8b57 in VCA::Project::stlSize (this=0x7f518d9a8d20) at project.cpp:456
#10 0x00007f518f1a9585 in VCA::Project::stlPropSet (this=0x7f518d9a8d20, pid=..., vl=..., sid=0) at project.cpp:521
#11 0x00007f518f1c431b in VCA::Page::stlReq (this=0x7f518c6d9d50, a=..., vl=..., wr=true) at project.cpp:1566
#12 0x00007f518f1376e2 in VCA::Widget::stlReq (this=0x7f518bdd5bb0, a=..., vl=..., wr=true) at widget.cpp:858
#13 0x00007f518f159026 in VCA::Attr::setS (this=0x7f518bcc2d70, val=..., strongPrev=true, sys=false) at widget.cpp:1943
...
#41 0x00007f51904b308b in start (p=0x7f518d80cad8) at src/thread/pthread_create.c:203
#42 0x00007f51904b538e in __clone () at src/thread/x86_64/clone.s:22


UserProtocol: Вызов setEnable производит захват на запись, а вызов loadIO следом пытается получить права на чтение, что приводит к дедлоку:

JAVASCRIPT
Thread 1 (LWP 7):
#0  __syscall_cp_c (nr=202, u=139988254130728, v=128, w=-1, x=0, y=0, z=0) at ./arch/x86_64/syscall_arch.h:61
#1  0x00007f51904b1ba9 in __futex4_cp (to=0x0, val=-1, op=128, addr=0x7f518e290228) at src/thread/__timedwait.c:24
#2  __timedwait_cp (addr=addr@entry=0x7f518e290228, val=val@entry=-1, clk=clk@entry=0, at=at@entry=0x0, priv=priv@entry=128) at src/thread/__timedwait.c:52
#3  0x00007f51904b1c4e in __timedwait (addr=addr@entry=0x7f518e290228, val=-1, clk=clk@entry=0, at=at@entry=0x0, priv=128) at src/thread/__timedwait.c:68
#4  0x00007f51904b440c in __pthread_rwlock_timedrdlock (at=<optimized out>, rw=<optimized out>) at src/thread/pthread_rwlock_timedrdlock.c:18
#5  __pthread_rwlock_timedrdlock (rw=0x7f518e290228, at=0x0) at src/thread/pthread_rwlock_timedrdlock.c:3
#6  0x00007f51900ce9c4 in OSCADA::ResRW::resRequestR (this=0x7f518e290228, tm=0) at resalloc.cpp:86
#7  0x00007f51900cef94 in OSCADA::ResAlloc::request (this=0x7ffd797a7a60, write=false, tm=0) at resalloc.cpp:132
#8  0x00007f51900ceefc in OSCADA::ResAlloc::ResAlloc (this=0x7ffd797a7a60, rid=..., write=false, tm=0) at resalloc.cpp:122
#9  0x00007f518ebc4101 in UserProtocol::UserPrt::loadIO (this=0x7f518e28ff60) at user_prt.cpp:463
#10 0x00007f518ebc766e in UserProtocol::UserPrt::setEnable (this=0x7f518e28ff60, vl=true) at user_prt.cpp:602
#11 0x00007f518ebbeecd in UserProtocol::TProt::modStart (this=0x7f518f29e9b0) at user_prt.cpp:162
#12 0x00007f5190206733 in OSCADA::TSubSYS::subStart (this=0x7f518f29fb10) at tsubsys.cpp:120
#13 0x00007f51900858be in OSCADA::TSYS::start (this=0x7f51904f70c0) at tsys.cpp:912
#14 0x000055d8151669b6 in main (argc=5, argv=0x7ffd797a82f8, envp=0x7ffd797a8328) at main.cpp:90


WebUser: Вызов setEnable производит захват на запись, а вызов loadIO следом пытается получить права на чтение, что приводит к дедлоку:

JAVASCRIPT
Thread 1 (LWP 7):
#0  __syscall_cp_c (nr=202, u=140208415574280, v=128, w=-1, x=0, y=0, z=0) at ./arch/x86_64/syscall_arch.h:61
#1  0x00007f84d2edeba9 in __futex4_cp (to=0x0, val=-1, op=128, addr=0x7f84d0ce0908) at src/thread/__timedwait.c:24
#2  __timedwait_cp (addr=addr@entry=0x7f84d0ce0908, val=val@entry=-1, clk=clk@entry=0, at=at@entry=0x0, priv=priv@entry=128) at src/thread/__timedwait.c:52
#3  0x00007f84d2edec4e in __timedwait (addr=addr@entry=0x7f84d0ce0908, val=-1, clk=clk@entry=0, at=at@entry=0x0, priv=128) at src/thread/__timedwait.c:68
#4  0x00007f84d2ee140c in __pthread_rwlock_timedrdlock (at=<optimized out>, rw=<optimized out>) at src/thread/pthread_rwlock_timedrdlock.c:18
#5  __pthread_rwlock_timedrdlock (rw=0x7f84d0ce0908, at=0x0) at src/thread/pthread_rwlock_timedrdlock.c:3
#6  0x00007f84d2afb9c4 in OSCADA::ResRW::resRequestR (this=0x7f84d0ce0908, tm=0) at resalloc.cpp:86
#7  0x00007f84d2afbf94 in OSCADA::ResAlloc::request (this=0x7fffc5ec59b0, write=false, tm=0) at resalloc.cpp:132
#8  0x00007f84d2afbefc in OSCADA::ResAlloc::ResAlloc (this=0x7fffc5ec59b0, rid=..., write=false, tm=0) at resalloc.cpp:122
#9  0x00007f84d1552f07 in WebUser::UserPg::loadIO (this=0x7f84d0ce0650) at web_user.cpp:551
#10 0x00007f84d1556daa in WebUser::UserPg::setEnable (this=0x7f84d0ce0650, vl=true) at web_user.cpp:677
#11 0x00007f84d154a079 in WebUser::TWEB::modStart (this=0x7f84d1520040) at web_user.cpp:159
#12 0x00007f84d2c33733 in OSCADA::TSubSYS::subStart (this=0x7f84d1cc19e0) at tsubsys.cpp:120
#13 0x00007f84d2ab28be in OSCADA::TSYS::start (this=0x7f84d2f240c0) at tsys.cpp:912
#14 0x0000564c086bc9b6 in main (argc=5, argv=0x7fffc5ec6278, envp=0x7fffc5ec62a8) at main.cpp:90


Я исправил это, пересобрал систему и все отлично работает, дедлоков нет.
Не загрузил свой патч по той причине, что не уверен верно ли сделал, так как еще не разбирался в тонкостях работы сущностей и реализации, сделал как было очевидно на первый взгляд.

Заранее благодарю.

# rm -rf /bin/laden
Written on: 01. 05. 2023 [21:32]
roman
Roman Savochenko
Moderator
Contributor
Developer
registered since: 12.12.2007
Posts: 3750
Не ошибка, если учесть, что класс ResRW расширен для обработки таких блокировок ввиду сложности их отслеживания и которые иногда неминучи, как в случає с рекурсивными мютексами.

Данные-ж действительно можно обойти, но думаю они далеко не все!

Адаптируйте ResRW под этот LibC — http://oscada.org/svn/trunk/OpenSCADA/src/resalloc.h

Learn, learn and learn better than work, work and work.
Written on: 01. 05. 2023 [22:25]
dudanov
Sergey Dudanov
Topic creator
registered since: 14.08.2013
Posts: 26
Смотрю resalloc.cpp. Похоже, что код уже адаптирован. Просто к условиям #if !__GLIBC_PREREQ(2,4) можно добавить || __MUSL__.

Получится так для примера.
JAVASCRIPT
#if !__GLIBC_PREREQ(2,4) || __MUSL__
    wThr = 0;
#endif


Попробую. При удачных тестах пришлю патч в эту тему на Ваше рассмотрение.
Заранее благодарю, Роман.

# rm -rf /bin/laden
Written on: 01. 05. 2023 [23:32]
dudanov
Sergey Dudanov
Topic creator
registered since: 14.08.2013
Posts: 26
Да, как и предполагал все идеально работает. Так что решение у Вас уже было.

Патч простой. Вызов тривиального sed для файлов resalloc.[h,cpp], которые добавляют еще одно условие препроцессора.

JAVASCRIPT
$ sed '/^#if !__GLIBC_PREREQ(2,4)$/ s/$/ || __MUSL__/'  resalloc.h
$ sed '/^#if !__GLIBC_PREREQ(2,4)$/ s/$/ || __MUSL__/'  resalloc.cpp


Да, musl не определяет этот макрос, поэтому при сборке необходимо указать в опциях компиляции -D__MUSL__.
Если посчитаете нужным - добавьте в следующий релиз. Можно будет собирать под musl.

P.S.: Собираю образы Docker на основе Alpine Linux, использующего musl. Получается очень компактно и шустро. Упакованный образ занимает всего 13Мб без модулей использующих Qt. Развертывание на сервере за пару секунд.

# rm -rf /bin/laden
Written on: 02. 05. 2023 [06:29]
roman
Roman Savochenko
Moderator
Contributor
Developer
registered since: 12.12.2007
Posts: 3750
Реально только здесь, что было добавлено для Android где нет __GLIBC_PREREQ:

#ifndef __GLIBC_PREREQ
# define __GLIBC_PREREQ(maj, min) 1
#endif


Learn, learn and learn better than work, work and work.



7615