blob: 7e1a79b28c057fc569399713cf1d9d0d24917456 [file] [log] [blame]
#!/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")