| #!/usr/bin/env python3 |
| |
| # Copyright 2025 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # This script is used to list files that require `#pragma allow_unsafe_buffers` |
| # in the Chromium codebase. |
| # |
| # Prerequisites: |
| # -------------- |
| # Ensure your .gclient contains: |
| # ``` |
| # target_os = ["win", "android", "linux", "chromeos", "mac", "fuchsia"] |
| # ``` |
| # |
| # Usage for automatic spanification |
| # --------------------------------- |
| # By running this script before and after a spanification, we can determine |
| # which files have been fixed. (not 100% exhaustive). |
| # |
| # Example: |
| # |
| # 1. Checkout "main" |
| # 2. Generate a spanification patch: "rewrite". |
| # 3. Generate the pragma after spanification: "pragma-after". |
| # 5. Checkout "main". |
| # 4. Generate the pragma before spanification: "pragma-before". |
| # 6. Checkout "rewrite". |
| # 7. Generate the list of files that have been fixed: This is the pragma removed |
| # by "rewrite-after" but kept by "rewrite-before". |
| # ``` |
| # comm -13 \ |
| # <(git show --pretty="" --name-only pragma-before | sort -u) \ |
| # <(git show --pretty="" --name-only pragma-after | sort -u) \ |
| # > fixed |
| # ``` |
| # 8. Apply the changes to the files. |
| # ``` |
| # for file in $(cat fixed); do |
| # git diff pragma-after -- $file | git apply |
| # done |
| # 9. Commit "fixed". |
| # |
| # Illustration: |
| # |
| # main -> rewrite --------------------------------> fixed |
| # | | ^ |
| # | `-> pragma-after --. | |
| # | |-> comm-13-/ |
| # `-----------> pragma-before --' |
| # |
| import os |
| import subprocess |
| |
| # Make sure dependencies are up to date. |
| os.system("gclient sync -f -D") |
| |
| # Building is going to fail on multiple files. They will be fixed automatically |
| # inserting `opt_out_lines` in the file after the copyright notice. |
| opt_out_lines = [ |
| "", |
| "#ifdef UNSAFE_BUFFERS_BUILD", |
| "// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.", |
| "#pragma allow_unsafe_buffers", |
| "#endif", |
| ] |
| |
| # Before adding the opt_out lines, we need to clear them in every files. Note |
| # that the opt_out_lines are not very stable, the bug and comments might vary. |
| # We should delete the whole block. |
| # |
| # It is important to modify only the files in the git repository. We can use |
| # `git grep 'allow_unsafe_buffers'` to get the list of files. |
| print("Removing opt_out from files...") |
| files_with_pragma = """ |
| git grep 'allow_unsafe_buffers' \ |
| | cut -d':' -f1 \ |
| | sort -u |
| """ |
| for file in subprocess.check_output(files_with_pragma, shell=True)\ |
| .decode("utf-8")\ |
| .split("\n"): |
| if not file: |
| continue |
| |
| try: |
| with open(file, 'r') as f: |
| lines = f.readlines() |
| |
| with open(file, 'w') as f: |
| in_opt_out = False |
| for line in lines: |
| if in_opt_out: |
| if "#endif" in line: |
| in_opt_out = False |
| else: |
| if "#ifdef UNSAFE_BUFFERS_BUILD" in line: |
| in_opt_out = True |
| else: |
| f.write(line) |
| except Exception as e: |
| print("Failed to remove opt_out from %s: %s" % (file, str(e))) |
| |
| # Try building chromium on multiple target_os. |
| gn_args = {} |
| gn_args["linux"] = [ |
| 'target_os="linux"', |
| 'use_remoteexec=true', |
| 'dcheck_always_on=true', |
| 'is_asan=true', |
| 'optimize_for_fuzzing=true', |
| 'use_libfuzzer=true', |
| ] |
| gn_args["mac"] = [ |
| 'target_os="mac"', |
| 'use_remoteexec=true', |
| 'dcheck_always_on=true', |
| 'is_asan=true', |
| 'optimize_for_fuzzing=true', |
| 'use_libfuzzer=true', |
| ] |
| gn_args["win"] = [ |
| 'target_os="win"', |
| 'use_remoteexec=true', |
| 'dcheck_always_on=true', |
| 'is_asan=true', |
| 'optimize_for_fuzzing=true', |
| 'use_libfuzzer=true', |
| ] |
| gn_args["android"] = [ |
| 'target_os="android"', |
| 'use_remoteexec=true', |
| 'dcheck_always_on=true', |
| 'is_asan=true', |
| 'optimize_for_fuzzing=true', |
| 'use_libfuzzer=true', |
| ] |
| gn_args["fuchsia"] = [ |
| 'target_os="fuchsia"', |
| 'use_remoteexec=true', |
| 'dcheck_always_on=true', |
| 'is_asan=true', |
| 'optimize_for_fuzzing=true', |
| 'use_libfuzzer=true', |
| ] |
| gn_args["chromeos"] = [ |
| 'target_os="chromeos"', |
| 'use_remoteexec=true', |
| 'dcheck_always_on=true', |
| 'is_asan=true', |
| 'optimize_for_fuzzing=true', |
| 'use_libfuzzer=true', |
| ] |
| |
| for target, args in gn_args.items(): |
| print("Building for %s:" % target) |
| # Configure the target. |
| os.system("gn gen out/%s --args='%s'" % (target, "\n".join(args))) |
| |
| while True: |
| # Try building all the targets, exit if the build succeeds. Do not print the output. |
| if os.system("autoninja -C out/%s" % target) == 0: |
| print("Build succeeded for %s." % target) |
| break |
| |
| # Clang is reporting errors likes: |
| # <file>:<line>:<column>: error: unsafe pointer arithmetic [-Werror,-Wunsafe-buffer-usage] |
| # |
| # On Windows, this will be: |
| # <file>(line,column): error: unsafe pointer arithmetic [-Werror,-Wunsafe-buffer-usage] |
| # <file>:<line>:<column>: error: unsafe pointer arithmetic [-Werror,-Wunsafe-buffer-usage] |
| # This is because the file is using unsafe buffer operations. |
| # We will fix this by inserting the opt_out_lines in the file. |
| |
| # Get the list of files with unsafe buffer operations. |
| unsafe_buffers_files = subprocess.check_output( |
| """ |
| autoninja -k 0 -C out/%s |\ |
| grep -E 'Wunsafe-buffer-usage' |\ |
| cut -d':' -f1 |\ |
| cut -d'(' -f1 |\ |
| sort -u |
| """ % target, |
| shell=True).decode("utf-8").split("\n") |
| |
| # Strip the ../../ from the file paths. |
| unsafe_buffers_files = [ |
| file.replace("../../", "") for file in unsafe_buffers_files |
| ] |
| |
| # Clean empty strings. |
| unsafe_buffers_files = [file for file in unsafe_buffers_files if file] |
| |
| rewrittens = [] |
| print("Unsafe buffer operations found in:") |
| for file in unsafe_buffers_files: |
| print(file) |
| |
| # Fix the files by inserting the opt_out_lines before the first line, not |
| # starting with //. |
| for file in unsafe_buffers_files: |
| try: |
| print("Opting out %s" % file) |
| if (not os.path.exists(file)): |
| print("File %s does not exist." % file) |
| continue |
| |
| with open(file, 'r') as f: |
| lines = f.readlines() |
| |
| with open(file, 'w') as f: |
| inserted = False |
| for line in lines: |
| if not inserted and not line.startswith("//"): |
| for opt_out_line in opt_out_lines: |
| f.write("%s\n" % opt_out_line) |
| inserted = True |
| f.write(line) |
| rewrittens.append(file) |
| except Exception as e: |
| print("Failed to opt out %s: %s" % (file, str(e))) |
| |
| if not rewrittens: |
| print("No files were fixed.") |
| break |
| |
| # Once it compiles on every targets, format the code, and create a commit. |
| os.system("git cl format") |
| os.system("git add -u") |
| |
| git_commit_description =\ |
| """spanification: Add `#pragma allow_unsafe_buffers` to xxx |
| |
| This is a preparation to fix each files. |
| This CL has no behavior changes. |
| |
| This patch was fully automated using script: |
| https://paste.googleplex.com/5614491201175552 |
| |
| See internal doc about it: |
| https://docs.google.com/document/d/1erdcokeh6rfBqs_h0drHqSLtbDbB61j7j3O2Pz8NH78/edit?resourcekey=0-hNe6w1hYAYyVXGEpWI7HVA&tab=t.0 |
| |
| Bug: 40285824""" |
| |
| with open("commit_description.txt", "w") as f: |
| f.write(git_commit_description) |
| |
| os.system("git commit -F commit_description.txt --no-edit") |