Skip to content

Commit

Permalink
Handle invalid UTF-8 characters in efibootmgr output
Browse files Browse the repository at this point in the history
Modified the code to handle invalid UTF-8 characters in the output of the efibootmgr command by
using `errors="ignore"`. This prevents decode exceptions when encountering non-UTF-8 sequences
in the EFI boot manager output.

This should be reverted when this is fixed upstream in the efibootmgr:
Possible fix: rhboot/efibootmgr#221

Resolves: rhbz#2254801
  • Loading branch information
KKoukiou committed Dec 19, 2024
1 parent 9c618ac commit df3b972
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 7 deletions.
22 changes: 16 additions & 6 deletions pyanaconda/core/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def preexec():


def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None,
ignore_decode_errors=False,
log_output=True, binary_output=False, filter_stderr=False,
do_preexec=True, env_add=None, user=None):
""" Run an external program, log the output and return it to the caller
Expand All @@ -182,6 +183,7 @@ def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None,
:param stdin: The file object to read stdin from.
:param stdout: Optional file object to write the output to.
:param env_prune: environment variable to remove before execution
:param ignore_decode_errors: whether to ignore decode errors
:param log_output: whether to log the output of command
:param binary_output: whether to treat the output of command as binary data
:param filter_stderr: whether to exclude the contents of stderr from the returned output
Expand All @@ -201,7 +203,10 @@ def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None,

(output_string, err_string) = proc.communicate()
if not binary_output:
output_string = output_string.decode("utf-8")
output_string = output_string.decode(
"utf-8",
errors="strict" if not ignore_decode_errors else "ignore"
)
if output_string and output_string[-1] != "\n":
output_string = output_string + "\n"

Expand Down Expand Up @@ -261,12 +266,14 @@ def execWithRedirect(command, argv, stdin=None, stdout=None, root='/',
:return: The return code of the command
"""
argv = [command] + argv
return _run_program(argv, stdin=stdin, stdout=stdout, root=root, env_prune=env_prune,
env_add=env_add, log_output=log_output, binary_output=binary_output,
return _run_program(argv, stdin=stdin, stdout=stdout, root=root,
env_prune=env_prune, env_add=env_add,
log_output=log_output, binary_output=binary_output,
do_preexec=do_preexec)[0]


def execWithCapture(command, argv, stdin=None, root='/', env_prune=None, env_add=None,
def execWithCapture(command, argv, stdin=None, root='/',
env_prune=None, env_add=None, ignore_decode_errors=False,
log_output=True, filter_stderr=False, do_preexec=True):
""" Run an external program and capture standard out and err.
Expand All @@ -276,15 +283,18 @@ def execWithCapture(command, argv, stdin=None, root='/', env_prune=None, env_add
:param root: The directory to chroot to before running command.
:param env_prune: environment variable to remove before execution
:param env_add: environment variables added for the execution
:param ignore_decode_errors: whether to ignore decode errors
:param log_output: Whether to log the output of command
:param filter_stderr: Whether stderr should be excluded from the returned output
:param do_preexec: whether to use the preexec function
:return: The output of the command
"""
argv = [command] + argv

return _run_program(argv, stdin=stdin, root=root, log_output=log_output, env_prune=env_prune,
env_add=env_add, filter_stderr=filter_stderr, do_preexec=do_preexec)[1]
return _run_program(argv, stdin=stdin, root=root, log_output=log_output,
env_prune=env_prune, env_add=env_add,
ignore_decode_errors=ignore_decode_errors,
filter_stderr=filter_stderr, do_preexec=do_preexec)[1]


def execWithCaptureAsLiveUser(command, argv, stdin=None, root='/', log_output=True,
Expand Down
4 changes: 3 additions & 1 deletion pyanaconda/modules/storage/bootloader/efi.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ def add_efi_boot_target(self):
self._add_single_efi_boot_target(parent)

def remove_efi_boot_target(self):
buf = self.efibootmgr(capture=True)
# FIXME: Stop using ignore_decode_errors=True once
# https://github.com/rhboot/efibootmgr/pull/221/ is merged
buf = self.efibootmgr(capture=True, ignore_decode_errors=True)
for line in buf.splitlines():
try:
(slot, _product) = line.split(None, 1)
Expand Down
15 changes: 15 additions & 0 deletions tests/unit_tests/pyanaconda_tests/core/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ def test_exec_with_capture_as_live_user(self, mock_get_live_user, mock_start_pro
assert mock_start_program.call_args.kwargs["env_add"] == {"TEST": "test"}
assert mock_start_program.call_args.kwargs["env_prune"] == ("TEST_PRUNE",)

def test_exec_with_capture_non_utf8_handling(self):
"""Test execWithCapture with non-utf8 output ignored."""

# Echo something that cannot be decoded as utf-8
output = util.execWithCapture(
'echo', ['-en', r'Hello world! \xa0\xa1\xa2'],
ignore_decode_errors=True
)

assert output == b'Hello world!'

# If ignore_non_utf8 is False (default), the non-utf8 output should raise an Exception
with pytest.raises(UnicodeDecodeError):
util.execWithCapture('echo', ['-en', r'\xa0\xa1\xa2'])

def test_exec_readlines(self):
"""Test execReadlines."""

Expand Down

0 comments on commit df3b972

Please sign in to comment.