summaryrefslogtreecommitdiffstats
path: root/bitbake/lib
diff options
context:
space:
mode:
authorJoshua Watt <JPEWhacker@gmail.com>2024-11-27 15:10:06 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2024-11-28 00:06:24 +0000
commit29da7370d219924a7f1fa106b13f601ec8795eab (patch)
treef240042cfb73e5afe2179524e7d702494c9bd300 /bitbake/lib
parentef5aaedf2a47a9d132557715381cdc879ec8f91b (diff)
downloadpoky-29da7370d219924a7f1fa106b13f601ec8795eab.tar.gz
bitbake: Remove custom exception backtrace formatting
Removes the code in bitbake to show custom backtrace formatting for exceptions. In particular, the bitbake exception code prints function arguments, which while helpful is a security problem when passwords and other secrets can be passed as function arguments. As it turns out, the handling of the custom serialized exception stack frames was pretty much made obsolete by d7db75020ed ("event/msg: Pass formatted exceptions"), which changed the events to pass a preformatted stacktrack list of strings, but the passing of the serialized data was never removed. Change all the code to use the python traceback API to format exceptions instead of the custom code; conveniently traceback.format_exception() also returns a list of stack trace strings, so it can be used as a drop in replacement for bb.exception.format_exception() (Bitbake rev: 2cda75a185aaf8f657f072dac34f8cef9d75f63a) Signed-off-by: Joshua Watt <JPEWhacker@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib')
-rw-r--r--bitbake/lib/bb/cooker.py32
-rw-r--r--bitbake/lib/bb/event.py9
-rw-r--r--bitbake/lib/bb/exceptions.py94
-rw-r--r--bitbake/lib/bb/msg.py4
-rw-r--r--bitbake/lib/bb/ui/teamcity.py5
5 files changed, 25 insertions, 119 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py
index a8e0a81dc9..ca37cfea95 100644
--- a/bitbake/lib/bb/cooker.py
+++ b/bitbake/lib/bb/cooker.py
@@ -17,7 +17,7 @@ import threading
17from io import StringIO, UnsupportedOperation 17from io import StringIO, UnsupportedOperation
18from contextlib import closing 18from contextlib import closing
19from collections import defaultdict, namedtuple 19from collections import defaultdict, namedtuple
20import bb, bb.exceptions, bb.command 20import bb, bb.command
21from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build 21from bb import utils, data, parse, event, cache, providers, taskdata, runqueue, build
22import queue 22import queue
23import signal 23import signal
@@ -2096,7 +2096,6 @@ class Parser(multiprocessing.Process):
2096 except Exception as exc: 2096 except Exception as exc:
2097 tb = sys.exc_info()[2] 2097 tb = sys.exc_info()[2]
2098 exc.recipe = filename 2098 exc.recipe = filename
2099 exc.traceback = list(bb.exceptions.extract_traceback(tb, context=3))
2100 return True, None, exc 2099 return True, None, exc
2101 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown 2100 # Need to turn BaseExceptions into Exceptions here so we gracefully shutdown
2102 # and for example a worker thread doesn't just exit on its own in response to 2101 # and for example a worker thread doesn't just exit on its own in response to
@@ -2297,8 +2296,12 @@ class CookerParser(object):
2297 return False 2296 return False
2298 except ParsingFailure as exc: 2297 except ParsingFailure as exc:
2299 self.error += 1 2298 self.error += 1
2300 logger.error('Unable to parse %s: %s' % 2299
2301 (exc.recipe, bb.exceptions.to_string(exc.realexception))) 2300 exc_desc = str(exc)
2301 if isinstance(exc, SystemExit) and not isinstance(exc.code, str):
2302 exc_desc = 'Exited with "%d"' % exc.code
2303
2304 logger.error('Unable to parse %s: %s' % (exc.recipe, exc_desc))
2302 self.shutdown(clean=False) 2305 self.shutdown(clean=False)
2303 return False 2306 return False
2304 except bb.parse.ParseError as exc: 2307 except bb.parse.ParseError as exc:
@@ -2307,20 +2310,33 @@ class CookerParser(object):
2307 self.shutdown(clean=False, eventmsg=str(exc)) 2310 self.shutdown(clean=False, eventmsg=str(exc))
2308 return False 2311 return False
2309 except bb.data_smart.ExpansionError as exc: 2312 except bb.data_smart.ExpansionError as exc:
2313 def skip_frames(f, fn_prefix):
2314 while f and f.tb_frame.f_code.co_filename.startswith(fn_prefix):
2315 f = f.tb_next
2316 return f
2317
2310 self.error += 1 2318 self.error += 1
2311 bbdir = os.path.dirname(__file__) + os.sep 2319 bbdir = os.path.dirname(__file__) + os.sep
2312 etype, value, _ = sys.exc_info() 2320 etype, value, tb = sys.exc_info()
2313 tb = list(itertools.dropwhile(lambda e: e.filename.startswith(bbdir), exc.traceback)) 2321
2322 # Remove any frames where the code comes from bitbake. This
2323 # prevents deep (and pretty useless) backtraces for expansion error
2324 tb = skip_frames(tb, bbdir)
2325 cur = tb
2326 while cur:
2327 cur.tb_next = skip_frames(cur.tb_next, bbdir)
2328 cur = cur.tb_next
2329
2314 logger.error('ExpansionError during parsing %s', value.recipe, 2330 logger.error('ExpansionError during parsing %s', value.recipe,
2315 exc_info=(etype, value, tb)) 2331 exc_info=(etype, value, tb))
2316 self.shutdown(clean=False) 2332 self.shutdown(clean=False)
2317 return False 2333 return False
2318 except Exception as exc: 2334 except Exception as exc:
2319 self.error += 1 2335 self.error += 1
2320 etype, value, tb = sys.exc_info() 2336 _, value, _ = sys.exc_info()
2321 if hasattr(value, "recipe"): 2337 if hasattr(value, "recipe"):
2322 logger.error('Unable to parse %s' % value.recipe, 2338 logger.error('Unable to parse %s' % value.recipe,
2323 exc_info=(etype, value, exc.traceback)) 2339 exc_info=sys.exc_info())
2324 else: 2340 else:
2325 # Most likely, an exception occurred during raising an exception 2341 # Most likely, an exception occurred during raising an exception
2326 import traceback 2342 import traceback
diff --git a/bitbake/lib/bb/event.py b/bitbake/lib/bb/event.py
index 4761c86880..952c85c0bd 100644
--- a/bitbake/lib/bb/event.py
+++ b/bitbake/lib/bb/event.py
@@ -19,7 +19,6 @@ import sys
19import threading 19import threading
20import traceback 20import traceback
21 21
22import bb.exceptions
23import bb.utils 22import bb.utils
24 23
25# This is the pid for which we should generate the event. This is set when 24# This is the pid for which we should generate the event. This is set when
@@ -759,13 +758,7 @@ class LogHandler(logging.Handler):
759 758
760 def emit(self, record): 759 def emit(self, record):
761 if record.exc_info: 760 if record.exc_info:
762 etype, value, tb = record.exc_info 761 record.bb_exc_formatted = traceback.format_exception(*record.exc_info)
763 if hasattr(tb, 'tb_next'):
764 tb = list(bb.exceptions.extract_traceback(tb, context=3))
765 # Need to turn the value into something the logging system can pickle
766 record.bb_exc_info = (etype, value, tb)
767 record.bb_exc_formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
768 value = str(value)
769 record.exc_info = None 762 record.exc_info = None
770 fire(record, None) 763 fire(record, None)
771 764
diff --git a/bitbake/lib/bb/exceptions.py b/bitbake/lib/bb/exceptions.py
deleted file mode 100644
index 60643bd642..0000000000
--- a/bitbake/lib/bb/exceptions.py
+++ /dev/null
@@ -1,94 +0,0 @@
1#
2# Copyright BitBake Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import inspect
8import traceback
9import bb.namedtuple_with_abc
10from collections import namedtuple
11
12
13class TracebackEntry(namedtuple.abc):
14 """Pickleable representation of a traceback entry"""
15 _fields = 'filename lineno function args code_context index'
16 _header = ' File "{0.filename}", line {0.lineno}, in {0.function}{0.args}'
17
18 def format(self, formatter=None):
19 if not self.code_context:
20 return self._header.format(self) + '\n'
21
22 formatted = [self._header.format(self) + ':\n']
23
24 for lineindex, line in enumerate(self.code_context):
25 if formatter:
26 line = formatter(line)
27
28 if lineindex == self.index:
29 formatted.append(' >%s' % line)
30 else:
31 formatted.append(' %s' % line)
32 return formatted
33
34 def __str__(self):
35 return ''.join(self.format())
36
37def _get_frame_args(frame):
38 """Get the formatted arguments and class (if available) for a frame"""
39 arginfo = inspect.getargvalues(frame)
40
41 if not arginfo.args:
42 return '', None
43
44 firstarg = arginfo.args[0]
45 if firstarg == 'self':
46 self = arginfo.locals['self']
47 cls = self.__class__.__name__
48
49 arginfo.args.pop(0)
50 try:
51 del arginfo.locals['self']
52 except TypeError:
53 # FIXME - python 3.13 FrameLocalsProxy can't be modified
54 pass
55 else:
56 cls = None
57
58 formatted = inspect.formatargvalues(*arginfo)
59 return formatted, cls
60
61def extract_traceback(tb, context=1):
62 frames = inspect.getinnerframes(tb, context)
63 for frame, filename, lineno, function, code_context, index in frames:
64 formatted_args, cls = _get_frame_args(frame)
65 if cls:
66 function = '%s.%s' % (cls, function)
67 yield TracebackEntry(filename, lineno, function, formatted_args,
68 code_context, index)
69
70def format_extracted(extracted, formatter=None, limit=None):
71 if limit:
72 extracted = extracted[-limit:]
73
74 formatted = []
75 for tracebackinfo in extracted:
76 formatted.extend(tracebackinfo.format(formatter))
77 return formatted
78
79
80def format_exception(etype, value, tb, context=1, limit=None, formatter=None):
81 formatted = ['Traceback (most recent call last):\n']
82
83 if hasattr(tb, 'tb_next'):
84 tb = extract_traceback(tb, context)
85
86 formatted.extend(format_extracted(tb, formatter, limit))
87 formatted.extend(traceback.format_exception_only(etype, value))
88 return formatted
89
90def to_string(exc):
91 if isinstance(exc, SystemExit):
92 if not isinstance(exc.code, str):
93 return 'Exited with "%d"' % exc.code
94 return str(exc)
diff --git a/bitbake/lib/bb/msg.py b/bitbake/lib/bb/msg.py
index 3e18596faa..4f616ff42e 100644
--- a/bitbake/lib/bb/msg.py
+++ b/bitbake/lib/bb/msg.py
@@ -89,10 +89,6 @@ class BBLogFormatter(logging.Formatter):
89 msg = logging.Formatter.format(self, record) 89 msg = logging.Formatter.format(self, record)
90 if hasattr(record, 'bb_exc_formatted'): 90 if hasattr(record, 'bb_exc_formatted'):
91 msg += '\n' + ''.join(record.bb_exc_formatted) 91 msg += '\n' + ''.join(record.bb_exc_formatted)
92 elif hasattr(record, 'bb_exc_info'):
93 etype, value, tb = record.bb_exc_info
94 formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
95 msg += '\n' + ''.join(formatted)
96 return msg 92 return msg
97 93
98 def colorize(self, record): 94 def colorize(self, record):
diff --git a/bitbake/lib/bb/ui/teamcity.py b/bitbake/lib/bb/ui/teamcity.py
index fca46c2874..7eeaab8d63 100644
--- a/bitbake/lib/bb/ui/teamcity.py
+++ b/bitbake/lib/bb/ui/teamcity.py
@@ -30,7 +30,6 @@ import bb.build
30import bb.command 30import bb.command
31import bb.cooker 31import bb.cooker
32import bb.event 32import bb.event
33import bb.exceptions
34import bb.runqueue 33import bb.runqueue
35from bb.ui import uihelper 34from bb.ui import uihelper
36 35
@@ -102,10 +101,6 @@ class TeamcityLogFormatter(logging.Formatter):
102 details = "" 101 details = ""
103 if hasattr(record, 'bb_exc_formatted'): 102 if hasattr(record, 'bb_exc_formatted'):
104 details = ''.join(record.bb_exc_formatted) 103 details = ''.join(record.bb_exc_formatted)
105 elif hasattr(record, 'bb_exc_info'):
106 etype, value, tb = record.bb_exc_info
107 formatted = bb.exceptions.format_exception(etype, value, tb, limit=5)
108 details = ''.join(formatted)
109 104
110 if record.levelno in [bb.msg.BBLogFormatter.ERROR, bb.msg.BBLogFormatter.CRITICAL]: 105 if record.levelno in [bb.msg.BBLogFormatter.ERROR, bb.msg.BBLogFormatter.CRITICAL]:
111 # ERROR gets a separate errorDetails field 106 # ERROR gets a separate errorDetails field