Safety concept¶
obd-diag should not be able to change anything in the car. For that there is a hard limit in the code and tests that check it from several sides (ADR 0002: Read-only, with an allowlist in the driver).
Allowlist¶
Elm327.command in protocol/elm327.py is the only way to the adapter. It only lets
through:
adapter commands that send nothing to the vehicle:
ATZ,ATE0,ATL0,ATS0,ATH0,ATH1,ATSP0,ATRV,ATDP,ATDPN, plus, read-only for the adapter check,ATI,AT@1,ATPPS,ATIGN,ATRD,ATAMC;read OBD requests:
01xx,02xxand02xx00,03,07,0A,0902;04(clearing) only insidewith elm.allow_clear(), and onlyobd.clear_dtcsuses that.
Everything else raises ForbiddenCommandError before anything is sent, including
lower case, spaces or an appended second command. The exception is deliberately not an
ElmError, so that no caller catches it as “value not available”. Every entry is
backed by a datasheet page in the Command reference.
Pace¶
There is only ever one request in flight: Elm327.command sends and then reads up to
the prompt >; only then can the next one go out. If a control unit reports
7F xx 78 (response pending), reading continues without sending again. Behind this is
a hard minimum gap: between the end of a response and the next request to the vehicle
(anything except AT…) there are at least MIN_REQUEST_GAP = 50 ms, so at most 20
requests per second, also after errors and timeouts and also when an adapter sends the
prompt too early. The standard would not even need a gap: according to ISO 15765-4 the
next request may follow immediately, and with K-Line the ELM327 keeps the minimum gap
P3 itself. Live data additionally queries at most one round every 0.1 s, below 11.8 V
only every 5 s. After a connection error or a missing response the procedure aborts
and sends nothing more.
Clearing¶
flowchart TD
G{CLEAR_ENABLED?} -- no --> X[Abort before opening the port]
G -- yes --> S[Scan and preconditions]
S --> F[Freeze frame]
F --> B[Backup on disk]
B --> M[Mode 04, exactly once]
M --> K[Check scan]
Lock:
CLEAR_ENABLED = Falseinservices/clear.pyuntil reading has been verified on a real car. It is only enabled after an explicit decision.Preconditions:
0100answers (ignition on), voltage not below 11.8 V (ifATRVshows less, the control module voltage PID 42 decides, if plausible), engine speed of every control unit validly 0. An unreadable engine speed is an abort.Backup first: mode 04 only goes out once the backup is completely on disk; existing backups are never overwritten.
Never repeated: mode 04 is sent at most once, also with
7F 04 78.
How this is checked¶
Guarantee |
Test |
|---|---|
allowlist == specification from the datasheet, exhaustively over all hex commands |
|
every command sent in any test is in the specification |
|
arbitrary text is allowed or never sent (Hypothesis); exact command sequence of every function |
|
clearing with arbitrarily broken responses: |
|
for every error case before clearing: |
|
live data with arbitrary responses: only |
|
call graph (AST): only intended places reach |
|
the shipped lock works in CLI and user interface |
|
never a request before the previous response, at least 50 ms between requests to the vehicle, also after errors, for a whole diagnosis and arbitrary command sequences |
|
line format: upper case, digits, exactly one CR, also via the real serial transport |
|
Mutation tests on services/clear.py detect all mutants; in elm327.py and obd.py
only mutants of texts and timeouts survive on the clearing path.
What is deliberately missing¶
No coding, no flashing, no manufacturer commands, no service functions (routines, actuators, adaptation values), no mode 08 (controlling components). Later extensions such as UDS remain read-only and go through their own allowlist (Roadmap).