libc: fix nested monitor call in 'symlink'

The symlink implementation wrongly constructed a 'Sync' object within
the context of a monitor call. The 'Sync' constructor indirectly
depended on libc I/O for obtaining the current time, ultimately
resulting in a nested attempt of a monitor call. This could be
reproduced via the base.run script:

  $ cd /home
  $ ln -s a b

The 'ln' command resulted in the following log message:

  [init -> /bin/bash -> 7] Error: deadlock ahead, mutex=0x10ff8c70, return ip=0x500583a7

The patch fixes the problem by splitting the single monitor call into
two monitor calls and moving the construction of the 'Sync' object
in-between both monitor calls, thereby executing the constructor at the
libc application level.

Fixes #4219
This commit is contained in:
Norman Feske 2021-07-12 14:34:16 +02:00 committed by Christian Helmuth
parent f3908b8283
commit 5138aeba80

View File

@ -1675,22 +1675,16 @@ int Libc::Vfs_plugin::fsync(File_descriptor *fd)
int Libc::Vfs_plugin::symlink(const char *target_path, const char *link_path)
{
enum class Stage { OPEN, WRITE, SYNC };
Stage stage { Stage::OPEN };
Vfs::Vfs_handle *handle { nullptr };
Constructible<Sync> sync;
Vfs::file_size const count { ::strlen(target_path) + 1 };
Vfs::file_size out_count { 0 };
{
bool succeeded { false };
int result_errno { 0 };
monitor().monitor([&] {
switch (stage) {
case Stage::OPEN:
{
typedef Vfs::Directory_service::Openlink_result Openlink_result;
Openlink_result openlink_result =
@ -1716,8 +1710,25 @@ int Libc::Vfs_plugin::symlink(const char *target_path, const char *link_path)
}
handle->handler(&_response_handler);
succeeded = true;
return Fn::COMPLETE;
});
if (!succeeded)
return Errno(result_errno);
}
/* must be done outside the monitor because constructor needs libc I/O */
sync.construct(*handle, _update_mtime, _current_real_time);
} stage = Stage::WRITE; [[fallthrough]];
{
bool succeeded { false };
int result_errno { 0 };
enum class Stage { WRITE, SYNC } stage = Stage::WRITE;
monitor().monitor([&] {
switch (stage) {
case Stage::WRITE:
{
@ -1745,6 +1756,7 @@ int Libc::Vfs_plugin::symlink(const char *target_path, const char *link_path)
if (!succeeded)
return Errno(result_errno);
}
return 0;
}