Recognize verified passive ELM327 diagnostic startup without waiving faults

This commit is contained in:
firestar5683
2026-09-19 22:05:14 -07:00
parent bd572108ab
commit 803de7b645
3 changed files with 84 additions and 3 deletions
@@ -0,0 +1,36 @@
# Passive observer safety evidence
The helper accepts two exact observed variants with genuine `CarParams.passive`,
configured noOutput/parameter0, inactive fresh control/state signals and fault-free
pandas: all actual noOutput, or all actual ELM327/parameter1 under the documented
passive startup conditions. It is not authorization, calibration acceptance,
coexistence acceptance, engagement support, or proof of zero CAN traffic.
The earlier assumption that normal passive startup must reach actual noOutput was
incorrect. Source in `selfdrive/pandad/panda_safety.cc` initializes every panda to
ELM327 parameter1 before waiting for FirmwareQueryDone and ControlsReady. Upstream
`selfdrive/car/card.py` sets ControlsReady inside controls_update, while passive
step skips controls_update. Thus the normal passive path can remain in ELM327.
Parameter1 means no OBD multiplexing; enabling OBD multiplexing changes the primary
panda parameter to0. Only the explicit parameter1 baseline is accepted here.
The collector supplies current read-only `passive_startup` values:
`controls_ready=false`, `firmware_query_done=true`,
`obd_multiplexing_enabled=false`. Missing helper evidence fails closed; native
Params absent-bool semantics are the collector's responsibility. No helper writes
these flags or attempts to force a safety transition.
**ELM327 is not noOutput.** `opendbc/safety/modes/elm327.h` permits eight-byte
messages to diagnostic addresses 0x600–0x7FF, 0x18DB33F1, 0x18DAxxF1, and0x24B.
For0x24B it additionally restricts the leading ISO-TP nibble to0–3. Ordinary
allowed diagnostic addresses accept arbitrary payload content; this must not be
described as an all-TX-disabled safety mode. The mode uses nooutput_init and
requires observed controlsAllowed=false, but has its own diagnostic TX hook.
RoadScore's observer integration must continue to publish no CAN/control messages.
Faults remain an independent blocker, including interruptRateCan2. ELM allowance
does not waive safetyRxChecksInvalid, stale/invalid messages, active/always-on
lateral flags, calibration, model health, local-model placement, resource/link
bounds or parked coexistence. Mixed observed safety modes are rejected as a
transition/unestablished baseline. Mode identity includes actual ELM parameter1,
so a noOutput authorization pin cannot silently carry over to ELM.
+13 -3
View File
@@ -41,9 +41,19 @@ def evaluate_passive_observer(snapshot, requested_mode):
pandas=snapshot.get('pandas')
if not isinstance(pandas,list) or not pandas:
reasons.append('Actual panda safety evidence is missing');pandas=[]
actual_modes={p.get('safetyModel') for p in pandas if isinstance(p,dict)}
elm=actual_modes=={'elm327'}
if len(actual_modes)>1:reasons.append('Mixed panda startup safety modes are not an established passive baseline')
if elm:
startup=_mapping(snapshot.get('passive_startup'))
if startup.get('controls_ready') is not False:reasons.append('ELM passive baseline requires actual ControlsReady=false')
if startup.get('firmware_query_done') is not True:reasons.append('ELM passive baseline requires completed firmware query')
if startup.get('obd_multiplexing_enabled') is not False:reasons.append('ELM parameter-1 baseline requires OBD multiplexing disabled')
for index,panda in enumerate(pandas):
if not isinstance(panda,dict):reasons.append(f'Panda {index} evidence is malformed');continue
if panda.get('safetyModel')!='noOutput':reasons.append(f'Panda {index} is not actually noOutput')
if panda.get('safetyModel') not in ('noOutput','elm327'):reasons.append(f'Panda {index} is not in a supported passive safety mode')
if panda.get('safetyModel')=='elm327' and (type(panda.get('safetyParam')) is not int or panda['safetyParam']!=1):
reasons.append(f'Panda {index} is not the expected ELM327 parameter-1 startup state')
if panda.get('controlsAllowed') is not False:reasons.append(f'Panda {index} permits or has unknown actuation')
if panda.get('faults')!=[]:reasons.append(f'Panda {index} fault state is not explicitly clear')
if panda.get('safetyRxChecksInvalid') is not False:reasons.append(f'Panda {index} safety receive checks are invalid or unknown')
@@ -60,10 +70,10 @@ def evaluate_passive_observer(snapshot, requested_mode):
# baseline hashes. Runtime freshness/booleans are validated above, not hashed.
material={'mode':MODE,'passive':cp.get('passive'),'notCar':cp.get('notCar'),
'dashcamOnly':cp.get('dashcamOnly'),'configured_safety':configs,
'actual_panda_safety':[p.get('safetyModel') if isinstance(p,dict) else None for p in pandas]}
'actual_panda_safety':[{'model':p.get('safetyModel'),'parameter':p.get('safetyParam') if p.get('safetyModel')=='elm327' else 0} if isinstance(p,dict) else None for p in pandas]}
mode_identity=hashlib.sha256(json.dumps(material,sort_keys=True,separators=(',',':')).encode()).hexdigest() if not reasons else None
return {'mode':MODE,'safety_eligible':not reasons,'reasons':reasons,'mode_identity':mode_identity,
'engagement_available':False,'label':'Passive observer — vehicle engagement unavailable'}
'engagement_available':False,'diagnostic_tx_possible':elm,'actual_passive_variant':'elm327-diagnostic' if elm else 'noOutput','label':'Passive observer — vehicle engagement unavailable'}
def authorization_mode_matches(record, assessment):
@@ -69,3 +69,38 @@ def test_explicit_mode_no_auto_inference_and_input_unchanged():
def test_malformed_fields_fail_closed(field):
snapshot=healthy();snapshot[field]=None
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
def elm_baseline():
snapshot=healthy();snapshot['pandas'][0].update(safetyModel='elm327',safetyParam=1)
snapshot['passive_startup']={'controls_ready':False,'firmware_query_done':True,'obd_multiplexing_enabled':False}
return snapshot
def test_exact_upstream_passive_elm_state_is_diagnostic_not_tx_disabled():
result=evaluate_passive_observer(elm_baseline(),MODE)
assert result['safety_eligible'] and result['diagnostic_tx_possible']
assert result['actual_passive_variant']=='elm327-diagnostic'
assert not result['engagement_available']
nooutput=evaluate_passive_observer(healthy(),MODE)
assert result['mode_identity']!=nooutput['mode_identity']
@pytest.mark.parametrize('field,value',[('controls_ready',True),('controls_ready',None),('firmware_query_done',False),('firmware_query_done',None),('obd_multiplexing_enabled',True),('obd_multiplexing_enabled',None)])
def test_elm_requires_exact_observed_startup_conditions(field,value):
snapshot=elm_baseline();snapshot['passive_startup'][field]=value
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
@pytest.mark.parametrize('parameter',(0,2,None,True,'1'))
def test_elm_parameter_mismatch_rejected(parameter):
snapshot=elm_baseline();snapshot['pandas'][0]['safetyParam']=parameter
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
def test_elm_does_not_waive_faults_or_active_flags():
for field,value in [('faults',['interruptRateCan2']),('controlsAllowed',True),('safetyRxChecksInvalid',True)]:
snapshot=elm_baseline();snapshot['pandas'][0][field]=value
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
snapshot=elm_baseline();snapshot['control']['latActive']=True
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
snapshot=elm_baseline();snapshot['pandas'].append(deepcopy(healthy()['pandas'][0]))
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']