Merge lp:~mwhudson/ubuntu/focal/apport/lp-1854237 into lp:~ubuntu-core-dev/ubuntu/focal/apport/ubuntu

Proposed by Michael Hudson-Doyle
Status: Merged
Merged at revision: 2725
Proposed branch: lp:~mwhudson/ubuntu/focal/apport/lp-1854237
Merge into: lp:~ubuntu-core-dev/ubuntu/focal/apport/ubuntu
Diff against target: 220 lines (+49/-47)
6 files modified
apport/report.py (+19/-10)
data/apport (+13/-6)
debian/changelog (+12/-0)
etc/init.d/apport (+2/-2)
test/test_report.py (+3/-1)
test/test_signal_crashes.py (+0/-28)
To merge this branch: bzr merge lp:~mwhudson/ubuntu/focal/apport/lp-1854237
Reviewer Review Type Date Requested Status
Brian Murray Approve
Review via email: mp+376374@code.launchpad.net

Commit message

* Fix autopkgtest failures since recent security update: (LP: #1854237)
    - Fix regression in creating report for crashing setuid process by getting
      kernel to tell us the executable path rather than reading
      /proc/[pid]/exe.
    - Fix deletion of partially written core files.
    - Fix test_get_logind_session to use new API.
    - Restore add_proc_info raising ValueError for a dead process.
    - Delete test_lock_symlink, no longer applicable now that the lock is
      created in a directory only root can write to.

To post a comment you must log in.
2728. By Michael Hudson-Doyle

Restore add_proc_info raising ValueError for a dead process.

2729. By Michael Hudson-Doyle

Delete test_lock_symlink, no longer applicable now that the lock is
created in a directory only root can write to.

2730. By Michael Hudson-Doyle

fix test_get_logind_session

apport.Report.get_logind_session now takes a proc_pid_fd, not a pid

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

This branch now passes the autopkgtests in my testing \o/

Revision history for this message
Brian Murray (brian-murray) wrote :

This looks great! Thanks for taking the time to dig into it and sort it out properly.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'apport/report.py'
--- apport/report.py 2019-12-03 03:18:16 +0000
+++ apport/report.py 2019-12-05 02:42:15 +0000
@@ -541,7 +541,15 @@
541 if not self.pid:541 if not self.pid:
542 self.pid = int(pid)542 self.pid = int(pid)
543 pid = str(pid)543 pid = str(pid)
544 proc_pid_fd = os.open('/proc/%s' % pid, os.O_RDONLY | os.O_PATH | os.O_DIRECTORY)544 try:
545 proc_pid_fd = os.open('/proc/%s' % pid, os.O_RDONLY | os.O_PATH | os.O_DIRECTORY)
546 except (PermissionError, OSError, FileNotFoundError) as e:
547 if e.errno in (errno.EPERM, errno.EACCES):
548 raise ValueError('not accessible')
549 if e.errno == errno.ENOENT:
550 raise ValueError('invalid process')
551 else:
552 raise
545553
546 try:554 try:
547 self['ProcCwd'] = os.readlink('cwd', dir_fd=proc_pid_fd)555 self['ProcCwd'] = os.readlink('cwd', dir_fd=proc_pid_fd)
@@ -551,15 +559,16 @@
551 self['ProcStatus'] = _read_file('status', dir_fd=proc_pid_fd)559 self['ProcStatus'] = _read_file('status', dir_fd=proc_pid_fd)
552 self['ProcCmdline'] = _read_file('cmdline', dir_fd=proc_pid_fd).rstrip('\0')560 self['ProcCmdline'] = _read_file('cmdline', dir_fd=proc_pid_fd).rstrip('\0')
553 self['ProcMaps'] = _read_maps(proc_pid_fd)561 self['ProcMaps'] = _read_maps(proc_pid_fd)
554 try:562 if 'ExecutablePath' not in self:
555 self['ExecutablePath'] = os.readlink('exe', dir_fd=proc_pid_fd)563 try:
556 except (PermissionError, OSError, FileNotFoundError) as e:564 self['ExecutablePath'] = os.readlink('exe', dir_fd=proc_pid_fd)
557 if e.errno in (errno.EPERM, errno.EACCES):565 except (PermissionError, OSError, FileNotFoundError) as e:
558 raise ValueError('not accessible')566 if e.errno in (errno.EPERM, errno.EACCES):
559 if e.errno == errno.ENOENT:567 raise ValueError('not accessible')
560 raise ValueError('invalid process')568 if e.errno == errno.ENOENT:
561 else:569 raise ValueError('invalid process')
562 raise570 else:
571 raise
563 for p in ('rofs', 'rwfs', 'squashmnt', 'persistmnt'):572 for p in ('rofs', 'rwfs', 'squashmnt', 'persistmnt'):
564 if self['ExecutablePath'].startswith('/%s/' % p):573 if self['ExecutablePath'].startswith('/%s/' % p):
565 self['ExecutablePath'] = self['ExecutablePath'][len('/%s' % p):]574 self['ExecutablePath'] = self['ExecutablePath'][len('/%s' % p):]
566575
=== modified file 'data/apport'
--- data/apport 2019-12-03 03:18:16 +0000
+++ data/apport 2019-12-05 02:42:15 +0000
@@ -211,7 +211,7 @@
211 if limit > 0 and core_size > limit:211 if limit > 0 and core_size > limit:
212 error_log('aborting core dump writing, size %i exceeds current limit' % core_size)212 error_log('aborting core dump writing, size %i exceeds current limit' % core_size)
213 os.close(core_file)213 os.close(core_file)
214 os.unlink(core_path)214 os.unlink(core_path, dir_fd=cwd)
215 return215 return
216 error_log('writing core dump %s of size %i' % (core_path, core_size))216 error_log('writing core dump %s of size %i' % (core_path, core_size))
217 os.write(core_file, r['CoreDump'])217 os.write(core_file, r['CoreDump'])
@@ -227,17 +227,16 @@
227 if limit > 0 and written > limit:227 if limit > 0 and written > limit:
228 error_log('aborting core dump writing, size exceeds current limit %i' % limit)228 error_log('aborting core dump writing, size exceeds current limit %i' % limit)
229 os.close(core_file)229 os.close(core_file)
230 os.unlink(core_path)230 os.unlink(core_path, dir_fd=cwd)
231 return231 return
232 if os.write(core_file, block) != size:232 if os.write(core_file, block) != size:
233 error_log('aborting core dump writing, could not write')233 error_log('aborting core dump writing, could not write')
234 os.close(core_file)234 os.close(core_file)
235 os.unlink(core_path)235 os.unlink(core_path, dir_fd=cwd)
236 return236 return
237 block = os.read(0, 1048576)237 block = os.read(0, 1048576)
238238
239 os.close(core_file)239 os.close(core_file)
240 return core_path
241240
242241
243def usable_ram():242def usable_ram():
@@ -340,7 +339,7 @@
340def parse_arguments():339def parse_arguments():
341 parser = argparse.ArgumentParser(epilog="""340 parser = argparse.ArgumentParser(epilog="""
342 Alternatively, the following command line is understood for legacy hosts:341 Alternatively, the following command line is understood for legacy hosts:
343 <pid> <signal number> <core file ulimit> <dump mode> [global pid]342 <pid> <signal number> <core file ulimit> <dump mode> [global pid] [exe path]
344 """)343 """)
345344
346 # TODO: Use type=int345 # TODO: Use type=int
@@ -349,6 +348,7 @@
349 parser.add_argument("-c", "--core-ulimit", help="core ulimit (%%c)")348 parser.add_argument("-c", "--core-ulimit", help="core ulimit (%%c)")
350 parser.add_argument("-d", "--dump-mode", help="dump mode (%%d)")349 parser.add_argument("-d", "--dump-mode", help="dump mode (%%d)")
351 parser.add_argument("-P", "--global-pid", nargs='?', help="pid in root namespace (%%P)")350 parser.add_argument("-P", "--global-pid", nargs='?', help="pid in root namespace (%%P)")
351 parser.add_argument("-E", "--executable-path", nargs='?', help="path of executable (%%E)")
352352
353 options, rest = parser.parse_known_args()353 options, rest = parser.parse_known_args()
354354
@@ -356,7 +356,7 @@
356 for arg in rest:356 for arg in rest:
357 error_log("Unknown argument: %s", arg)357 error_log("Unknown argument: %s", arg)
358358
359 elif len(rest) in (4, 5):359 elif len(rest) in (4, 5, 6):
360 # Translate legacy command line360 # Translate legacy command line
361 options.pid = rest[0]361 options.pid = rest[0]
362 options.signal_number = rest[1]362 options.signal_number = rest[1]
@@ -366,6 +366,10 @@
366 options.global_pid = rest[4]366 options.global_pid = rest[4]
367 except IndexError:367 except IndexError:
368 options.global_pid = None368 options.global_pid = None
369 try:
370 options.exe_path = rest[5].replace('!', '/')
371 except IndexError:
372 options.exe_path = None
369 else:373 else:
370 parser.print_usage()374 parser.print_usage()
371 sys.exit(1)375 sys.exit(1)
@@ -599,6 +603,9 @@
599 # We already need this here to figure out the ExecutableName (for scripts,603 # We already need this here to figure out the ExecutableName (for scripts,
600 # etc).604 # etc).
601605
606 if options.exe_path is not None and os.path.exists(options.exe_path):
607 info['ExecutablePath'] = options.exe_path
608
602 euid = os.geteuid()609 euid = os.geteuid()
603 egid = os.getegid()610 egid = os.getegid()
604 try:611 try:
605612
=== modified file 'debian/changelog'
--- debian/changelog 2019-12-04 21:24:04 +0000
+++ debian/changelog 2019-12-05 02:42:15 +0000
@@ -1,8 +1,20 @@
1apport (2.20.11-0ubuntu13) UNRELEASED; urgency=medium1apport (2.20.11-0ubuntu13) UNRELEASED; urgency=medium
22
3 [ Brian Murray ]
3 * Create additional symlinks to the source_linux.py apport package hook for4 * Create additional symlinks to the source_linux.py apport package hook for
4 many OEM kernels. Thanks to You-Sheng Yang for the patch. (LP: #1847967)5 many OEM kernels. Thanks to You-Sheng Yang for the patch. (LP: #1847967)
56
7 [ Michael Hudson-Doyle ]
8 * Fix autopkgtest failures since recent security update: (LP: #1854237)
9 - Fix regression in creating report for crashing setuid process by getting
10 kernel to tell us the executable path rather than reading
11 /proc/[pid]/exe.
12 - Fix deletion of partially written core files.
13 - Fix test_get_logind_session to use new API.
14 - Restore add_proc_info raising ValueError for a dead process.
15 - Delete test_lock_symlink, no longer applicable now that the lock is
16 created in a directory only root can write to.
17
6 -- Brian Murray <brian@ubuntu.com> Wed, 04 Dec 2019 13:21:43 -080018 -- Brian Murray <brian@ubuntu.com> Wed, 04 Dec 2019 13:21:43 -0800
719
8apport (2.20.11-0ubuntu12) focal; urgency=medium20apport (2.20.11-0ubuntu12) focal; urgency=medium
921
=== modified file 'etc/init.d/apport'
--- etc/init.d/apport 2019-05-16 19:37:48 +0000
+++ etc/init.d/apport 2019-12-05 02:42:15 +0000
@@ -52,9 +52,9 @@
5252
53 # Old compatibility mode, switch later to second one53 # Old compatibility mode, switch later to second one
54 if true; then54 if true; then
55 echo "|$AGENT %p %s %c %d %P" > /proc/sys/kernel/core_pattern55 echo "|$AGENT %p %s %c %d %P %E" > /proc/sys/kernel/core_pattern
56 else56 else
57 echo "|$AGENT -p%p -s%s -c%c -d%d -P%P" > /proc/sys/kernel/core_pattern57 echo "|$AGENT -p%p -s%s -c%c -d%d -P%P -E%E" > /proc/sys/kernel/core_pattern
58 fi58 fi
59 echo 2 > /proc/sys/fs/suid_dumpable59 echo 2 > /proc/sys/fs/suid_dumpable
60}60}
6161
=== modified file 'test/test_report.py'
--- test/test_report.py 2019-05-17 17:51:10 +0000
+++ test/test_report.py 2019-12-05 02:42:15 +0000
@@ -2274,7 +2274,9 @@
2274 self.assertEqual(expected, pr.crash_signature())2274 self.assertEqual(expected, pr.crash_signature())
22752275
2276 def test_get_logind_session(self):2276 def test_get_logind_session(self):
2277 ret = apport.Report.get_logind_session(os.getpid())2277 proc_pid_fd = os.open('/proc/%s' % os.getpid(), os.O_RDONLY | os.O_PATH | os.O_DIRECTORY)
2278 self.addCleanup(os.close, proc_pid_fd)
2279 ret = apport.Report.get_logind_session(proc_pid_fd)
2278 if ret is None:2280 if ret is None:
2279 # ensure that we don't run under logind, and thus the None is2281 # ensure that we don't run under logind, and thus the None is
2280 # justified2282 # justified
22812283
=== modified file 'test/test_signal_crashes.py'
--- test/test_signal_crashes.py 2018-05-09 23:30:27 +0000
+++ test/test_signal_crashes.py 2019-12-05 02:42:15 +0000
@@ -192,34 +192,6 @@
192 os.kill(test_proc2, 9)192 os.kill(test_proc2, 9)
193 os.waitpid(test_proc2, 0)193 os.waitpid(test_proc2, 0)
194194
195 def test_lock_symlink(self):
196 '''existing .lock file as dangling symlink does not create the file
197
198 This would be a vulnerability, as users could overwrite system files.
199 '''
200 # prepare a symlink trap
201 lockpath = os.path.join(self.report_dir, '.lock')
202 trappath = os.path.join(self.report_dir, '0wned')
203 os.symlink(trappath, lockpath)
204
205 # now call apport
206 test_proc = self.create_test_process()
207
208 try:
209 app = subprocess.Popen([apport_path, str(test_proc), '42', '0', '1'],
210 stdin=subprocess.PIPE, stderr=subprocess.PIPE)
211 app.stdin.write(b'boo')
212 app.stdin.close()
213
214 self.assertNotEqual(app.wait(), 0, app.stderr.read())
215 app.stderr.close()
216 finally:
217 os.kill(test_proc, 9)
218 os.waitpid(test_proc, 0)
219
220 self.assertEqual(self.get_temp_all_reports(), [])
221 self.assertFalse(os.path.exists(trappath))
222
223 def test_unpackaged_binary(self):195 def test_unpackaged_binary(self):
224 '''unpackaged binaries do not create a report'''196 '''unpackaged binaries do not create a report'''
225197

Subscribers

People subscribed via source and target branches