Skip to content

Commit

Permalink
chardev/parallel: Don't close stdin on inappropriate device
Browse files Browse the repository at this point in the history
The __linux__ version of qemu_chr_open_pp_fd() tries to claim the
parport device with a PPCLAIM ioctl().  On success, it stores the file
descriptor in the chardev object, and returns success.  On failure, it
closes the file descriptor, and returns failure.

chardev_new() then passes the Chardev to object_unref().  This duly
calls char_parallel_finalize(), which closes the file descriptor
stored in the chardev object.  Since qemu_chr_open_pp_fd() didn't
store it, it's still zero, so this closes standard input.  Ooopsie.

To demonstate, add a unit test.  With the bug above unfixed, running
this test closes standard input.  char_hotswap_test() happens to run
next.  It opens a socket, duly gets file descriptor 0, and since it
tests for success with > 0 instead of >= 0, it fails.

The new unit test needs to be conditional exactly like the chardev it
tests.  Since the condition is rather complicated, steal the solution
from the serial chardev: define HAVE_CHARDEV_PARALLEL in qemu/osdep.h.
This also permits simplifying chardev/meson.build a bit.

The bug fix is easy enough: store the file descriptor, and leave
closing it to char_parallel_finalize().

The next commit will fix char_hotswap_test()'s test for success.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-ID: <20240203080228.2766159-2-armbru@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[Indentation fixed up, commit message improved]
  • Loading branch information
Markus Armbruster committed Feb 12, 2024
1 parent 5d1fc61 commit 785d155
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 6 deletions.
7 changes: 5 additions & 2 deletions chardev/char-parallel.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,13 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
{
ParallelChardev *drv = PARALLEL_CHARDEV(chr);

drv->fd = fd;

if (ioctl(fd, PPCLAIM) < 0) {
error_setg_errno(errp, errno, "not a parallel port");
close(fd);
return;
}

drv->fd = fd;
drv->mode = IEEE1284_MODE_COMPAT;
}
#endif /* __linux__ */
Expand Down Expand Up @@ -238,6 +238,7 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
}
#endif

#ifdef HAVE_CHARDEV_PARALLEL
static void qmp_chardev_open_parallel(Chardev *chr,
ChardevBackend *backend,
bool *be_opened,
Expand Down Expand Up @@ -306,3 +307,5 @@ static void register_types(void)
}

type_init(register_types);

#endif /* HAVE_CHARDEV_PARALLEL */
4 changes: 1 addition & 3 deletions chardev/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ if host_os == 'windows'
else
chardev_ss.add(files(
'char-fd.c',
'char-parallel.c',
'char-pty.c',
), util)
if host_os in ['linux', 'gnu/kfreebsd', 'freebsd', 'dragonfly']
chardev_ss.add(files('char-parallel.c'))
endif
endif

chardev_ss = chardev_ss.apply({})
Expand Down
9 changes: 8 additions & 1 deletion include/qemu/osdep.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,11 +508,18 @@ void qemu_anon_ram_free(void *ptr, size_t size);

#ifdef _WIN32
#define HAVE_CHARDEV_SERIAL 1
#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
#define HAVE_CHARDEV_PARALLEL 1
#else
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|| defined(__GLIBC__) || defined(__APPLE__)
#define HAVE_CHARDEV_SERIAL 1
#endif
#if defined(__linux__) || defined(__FreeBSD__) \
|| defined(__FreeBSD_kernel__) || defined(__DragonFly__)
#define HAVE_CHARDEV_PARALLEL 1
#endif
#endif

#if defined(__HAIKU__)
#define SIGIO SIGPOLL
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/test-char.c
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,24 @@ static void char_serial_test(void)
}
#endif

#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
static void char_parallel_test(void)
{
QemuOpts *opts;
Chardev *chr;

opts = qemu_opts_create(qemu_find_opts("chardev"), "parallel-id",
1, &error_abort);
qemu_opt_set(opts, "backend", "parallel", &error_abort);
qemu_opt_set(opts, "path", "/dev/null", &error_abort);

chr = qemu_chr_new_from_opts(opts, NULL, NULL);
g_assert_null(chr);

qemu_opts_del(opts);
}
#endif

#ifndef _WIN32
static void char_file_fifo_test(void)
{
Expand Down Expand Up @@ -1544,6 +1562,9 @@ int main(int argc, char **argv)
g_test_add_func("/char/udp", char_udp_test);
#if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32)
g_test_add_func("/char/serial", char_serial_test);
#endif
#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
g_test_add_func("/char/parallel", char_parallel_test);
#endif
g_test_add_func("/char/hotswap", char_hotswap_test);
g_test_add_func("/char/websocket", char_websock_test);
Expand Down

0 comments on commit 785d155

Please sign in to comment.