mirror of
https://github.com/speed47/spectre-meltdown-checker.git
synced 2026-09-15 15:20:45 +02:00
The old Intel HTML and XSLX files contain historical data from EOL processors that are no longer present in their Github repo. We merge these 3 sources to generate our db.
120 lines
4.5 KiB
Python
120 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Build INTELDB from a local clone of Intel's affected processor list history."""
|
|
|
|
import argparse
|
|
import csv
|
|
import io
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
from parse_inteldb_csv import format_cpuid, format_db, parse_csv
|
|
|
|
|
|
CSV_PATH = 'Intel_affected_processor_list.csv'
|
|
|
|
|
|
def parse_db(stream):
|
|
"""Recover legacy entries, adapted from compare_inteldb_history.py."""
|
|
entries = {}
|
|
in_db = False
|
|
for line in stream:
|
|
if '%%% INTELDB' in line:
|
|
in_db = True
|
|
continue
|
|
if not in_db:
|
|
continue
|
|
if '%%% ENDOFINTELDB' in line:
|
|
break
|
|
if not line.startswith('# 0x'):
|
|
continue
|
|
fields = line[2:].strip().rstrip(',').split(',')
|
|
cpuid = format_cpuid(fields.pop(0))
|
|
qualifier = ''
|
|
if fields and re.fullmatch(r'H=[01]', fields[0]):
|
|
qualifier = fields.pop(0)
|
|
vulns = {}
|
|
for field in fields:
|
|
cves, status = field.split('=', 1)
|
|
for cve in cves.split('+'):
|
|
vulns[cve] = status
|
|
entries[(cpuid, qualifier)] = vulns
|
|
if not entries:
|
|
raise ValueError('No entries found in baseline INTELDB')
|
|
return entries
|
|
|
|
|
|
def merge_snapshot(merged, snapshot):
|
|
"""Newest snapshot wins per CPUID; retain CPUs absent from that snapshot.
|
|
|
|
Replace all qualifiers for a present CPUID together. Otherwise historical
|
|
unqualified entries survive when explicit H=0/H=1 entries are introduced,
|
|
and runtime lookup can incorrectly fall back to obsolete data.
|
|
"""
|
|
present = {cpuid for cpuid, _ in snapshot}
|
|
for key in list(merged):
|
|
if key[0] in present:
|
|
del merged[key]
|
|
merged.update(snapshot)
|
|
|
|
|
|
def git(repo, *args):
|
|
return subprocess.run(
|
|
['git', '-C', repo, *args], check=True, stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE, encoding='utf-8-sig',
|
|
).stdout
|
|
|
|
|
|
def build_history(repo, ref='HEAD', baseline=None):
|
|
# Resolve once so a concurrent fetch/checkout cannot change the input.
|
|
revision = git(repo, 'rev-parse', '--verify', f'{ref}^{{commit}}').strip()
|
|
if git(repo, 'rev-parse', '--is-shallow-repository').strip() == 'true':
|
|
raise ValueError('Intel repository is shallow; use a full clone to retain historical CPUs')
|
|
# Consume Git's ordering directly, never a glob sorted by commit hash.
|
|
commits = git(repo, 'log', '--reverse', '--topo-order', '--format=%H',
|
|
revision, '--', CSV_PATH).splitlines()
|
|
# Always parse the selected tip last, including when it is a merge commit
|
|
# that was omitted by path-history simplification.
|
|
commits = [commit for commit in commits if commit != revision] + [revision]
|
|
merged = dict(baseline or {})
|
|
for commit in commits:
|
|
try:
|
|
snapshot = parse_csv(io.StringIO(git(repo, 'show', f'{commit}:{CSV_PATH}')))
|
|
except (ValueError, csv.Error) as error:
|
|
raise ValueError(f'{commit}: {error}') from error
|
|
merge_snapshot(merged, snapshot)
|
|
print(f'{commit[:12]}: {len(snapshot)} entries; {len(merged)} retained', file=sys.stderr)
|
|
return merged, revision
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('repo', help='Full local clone of intel/Intel-affected-processor-list')
|
|
parser.add_argument('output_file', nargs='?', help='Defaults to stdout')
|
|
parser.add_argument('--ref', default='HEAD', help='Revision to build (default: HEAD)')
|
|
parser.add_argument('--base-db', help='Existing INTELDB retaining legacy CPUs absent from CSV history')
|
|
args = parser.parse_args()
|
|
try:
|
|
header = Path(__file__).resolve().with_name('header.txt').read_text(encoding='utf-8')
|
|
baseline = None
|
|
if args.base_db:
|
|
with open(args.base_db, encoding='utf-8') as stream:
|
|
baseline = parse_db(stream)
|
|
entries, revision = build_history(args.repo, args.ref, baseline)
|
|
output = format_db(entries, f'Merged Intel CSV history through {revision} (newest CPUID data wins)')
|
|
output = header.rstrip('\n') + '\n' + output
|
|
if args.output_file:
|
|
with open(args.output_file, 'w', encoding='utf-8') as stream:
|
|
stream.write(output)
|
|
else:
|
|
sys.stdout.write(output)
|
|
except subprocess.CalledProcessError as error:
|
|
parser.exit(1, f'ERROR: git: {error.stderr.strip()}\n')
|
|
except (OSError, ValueError, csv.Error) as error:
|
|
parser.exit(1, f'ERROR: {error}\n')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|