a502322188
손상된 .git 히스토리(missing tree)로 재초기화 후 작업트리 전체 커밋. .claude/ 만 제외(로컬 에이전트 설정). 구 저장소 백업(.git_corrupt_backup/) 포함. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
122 lines
10 KiB
Markdown
122 lines
10 KiB
Markdown
# HERV 펌웨어 정적분석 버그 리포트
|
|
|
|
작성일: 2026-06-15
|
|
대상: `program/User/*.c`, `*.h` (펌웨어 전체)
|
|
방식: 영역별 정적분석(읽기 전용). **코드 수정 없음 — 검토용 목록**
|
|
표기: ✓ = 작성자가 직접 코드 확인 / (분석) = 분석에서 도출(미재확인) / [의심] = 추가 검증 필요
|
|
|
|
---
|
|
|
|
## 0. 우선순위 요약 (먼저 고칠 것)
|
|
|
|
| # | 위치 | 심각도 | 한줄 | 검증 |
|
|
|---|------|--------|------|------|
|
|
| 1 | My_RJ2.c:617 | High | switch case 8 `break` 누락 → 공청3단 세팅이 4단 프리셋 덮어씀 | ✓ |
|
|
| 2 | My_RJ2.c:400 | High | `Command_request_type = 0;` 로 보류비트(HOOD/예약/Homenet래치) 전멸 | ✓ |
|
|
| 3 | My_bunbaeggi.c:869 | High | 예약시간 offset 오류: `[8]` 검사하고 값은 `[7]`(풍량)에서 읽음 | ✓ |
|
|
| 4 | MyControl.c:595 | High | 필터리셋 조건에 `Filter_timer_change` 중복(`Soja_timer_change` 오타) → 소자 리셋 누락 | (분석) |
|
|
| 5 | My_bunbaeggi.c:708·898 | Med | 수신 id(`buffer[3]`) 범위검증 없이 크기7 배열 인덱싱 → OOB 쓰기 가능 | (분석) |
|
|
| 6 | My_bunbaeggi.c:280 | Med | `Light_Bright[6]` 만 크기6(형제 배열은 7) → 거실2(id_2=6) 접근 시 OOB | ✓ |
|
|
| 7 | My_RJ2.c:110 | Med | `Reservation_process()` 주석처리 → 룸컨 예약(`Reserve_timer_sec`) 카운트다운 안 함 | ✓ |
|
|
|
|
---
|
|
|
|
## 1. 통신 / 프로토콜
|
|
|
|
### My_RJ2.c (룸컨)
|
|
- **[High] My_RJ2.c:617 — `case 8` break 누락 (fall-through)** ✓
|
|
- `case 8`(공청 3단 VSP) 끝에 `break`가 없어 `case 9`(4단)로 흘러감 → 공청 3단 세팅 시 4단 프리셋(`Test_Fan*_Air_4_dan`)까지 같은 값으로 오염.
|
|
- 제안: `case 8` 끝에 `break;` 추가.
|
|
- **[High] My_RJ2.c:400 — `Command_request_type = 0;` 전체 클리어** ✓
|
|
- `if(Command_request_type & TYPE_SEND_FLAG)` 블록에서 `= 0;` 후 `&= ~TYPE_SEND_FLAG;`(죽은 코드). SEND_FLAG만 소비해야 하는데 같은 사이클에 set된 `TYPE_HOOD_STATE`/`TYPE_RESERVATION`/병합된 `Homenet_RJ_Request` 비트가 한 번에 사라져 후드·예약 변경이 룸컨에 유실.
|
|
- 비교: line 690~의 같은 패턴은 `&= ~TYPE_SEND_FLAG`만 함(불일치).
|
|
- 제안: `= 0;` → `&= ~TYPE_SEND_FLAG;` 로 교체, 필요한 비트만 명시적으로 클리어.
|
|
- **[Med] My_RJ2.c:712 — `Filter_Reset_Flag |= buffer[7]` 마스크 누락**
|
|
- EVENT 경로(line 462)는 `& 0x01` 마스크를 쓰는데 여기선 통째 OR → 상위비트 오염으로 의도치 않은 필터 리셋 가능. 제안: `& 0x01` 적용.
|
|
- **[Med][의심] My_RJ2.c:702-705 — AUTO 수신 시 `Fan_Mode` 미갱신**
|
|
- else 분기에서 AUTO면 `Set_Fan_Mode/Fan_Mode`를 갱신 안 한 채 line 718 `Set_Fan_Mode==Fan_Mode` 비교 → TYPE_FAN_SPEED 비트가 옛값으로 클리어/유지되어 풍량 명령 어긋남 가능. EVENT 분기(414-415)와 기준 통일 검토.
|
|
|
|
### My_bunbaeggi.c (분배기/디퓨저 마스터)
|
|
- **[High] My_bunbaeggi.c:869 — 예약시간 byte offset 오류** ✓
|
|
- `if(Rx_bunbaegi_buffer[8] & 0x80){ Set_Reserve_timer_sec = (uint32_t)(Rx_bunbaegi_buffer[7] & 0x7f)*3600; }` — 요청비트는 `[8]`에서 보고 값은 `[7]`(풍량 바이트)에서 읽음 → 예약시간이 풍량값으로 들어감. 제안: `[8]`에서 읽기.
|
|
- **[Med] My_bunbaeggi.c:708·898 (Diffuser/EachRoomCon parsing) — 수신 id 범위검증 없음** (분석)
|
|
- `id = Rx_bunbaegi_buffer[3];` 후 `SEN66_*[id]`, `Diffuser_Power[id]` 등 크기7 배열에 상한 체크 없이 인덱싱. 오염된 id(≥7)면 OOB 쓰기로 전역 손상. 제안: `if(id < 7)` 가드.
|
|
- **[Med] My_bunbaeggi.c:183/191 — RoomCon `Packet_Length`가 절대 29가 안 됨** (분석)
|
|
- case 3에서 무조건 39, case 4 RoomCon 분기도 39(주석은 `//29byte`). → case 28의 29B 완료처리 분기가 죽은 코드. 29B 응답 파싱 불가. 제안: 의도대로 case 4에서 `Packet_Length = 29;`.
|
|
- **[Low] My_bunbaeggi.c:813 — VSP 저장 트리거가 case 3(BYPASS)에만 존재** (분석)
|
|
- VEN/AIR VSP를 룸컨에서 바꾸면 `EEP_Save_Flag` 미설정 → 재부팅 후 유실 가능. 제안: case 1/2에도 동일 저장 조건.
|
|
|
|
### My_Homenet.c (PC 대시보드)
|
|
- **[Low/설계확인] My_Homenet.c:366 — CTRL_VSP u16→u8 truncation** ✓
|
|
- `(uint8_t)(((uint16_t)pl[2]<<8)|pl[3])` 는 상위바이트를 시프트 후 캐스팅으로 버려 사실상 `pl[3]`(하위)만 사용. VSP가 0~255라 실사용은 무해하나, 의도 명확화를 위해 `pl[3]`만 쓰는 게 안전. (확정 손상 아님)
|
|
- **[Low/의심] My_Homenet.c:410 — `hn_apply_cmd(cmd, pl, HN_DATA_LEN)` 로 len 항상 240** (분석)
|
|
- 모든 `if(len >= N)` 검증이 무조건 통과 → 짧은 명령 의미검증이 사실상 무력. 고정 244B 구조라 오버런은 없음. 동작 영향 낮음.
|
|
|
|
### My_Hood.c (후드)
|
|
- **[Med] My_Hood.c:64 — 헤더 불일치 바이트를 버퍼에 기록** (분석)
|
|
- case 1에서 0x11 불일치 시 `Rx_hood_Pos=0` 후에도 `Rx_Hood_Buff[Pos++]=data` 실행 → 잘못된 바이트가 buffer[0]에 들어가 프레임 한 칸 밀림. 제안: 불일치 시 `Pos=0; break;` 로 즉시 반환.
|
|
|
|
---
|
|
|
|
## 2. 시스템 / 전원 / 예약
|
|
|
|
### main.c
|
|
- **[Low] main.c:116-119 — `Process_10ms` 재장전값 3000, 본문 비어있음(데드/오타)** ✓
|
|
- `Process_5ms`/`Process_10ms` 블록 모두 카운터만 재장전하고 작업 없음(no-op). 동작 영향은 없으나 `=3000`은 명백한 leftover. 제안: 블록 제거 또는 의도값 복원.
|
|
|
|
### My_system.c
|
|
- **[Med] My_system.c (예약 카운터 이원화) — `Reserve_Remain_Sec`(HomeNet) vs `Reserve_timer_sec`(룸컨)** ✓
|
|
- main.c 1초 루프는 `Reserve_Remain_Sec`만 감산. 룸컨용 `Reserve_timer_sec`를 감산하는 `Reservation_process()`는 **My_RJ2.c:110에서 주석처리** → 룸컨이 설정한 예약은 카운트다운/전원OFF가 안 됨. 제안: 카운터 단일화 또는 `Reservation_process()` 호출 복원.
|
|
- **[Med] My_system.c:728 부근 `sensor_level()` — `T[i] - db` unsigned 언더플로** (분석)
|
|
- `uint16_t` 임계-데드밴드. 대시보드로 임계를 작게 설정하면 래핑되어 단계판정 폭주. 정상 사양값에선 미발생. 제안: 뺄셈 전 `T>=db` 가드 또는 signed 후 0 클램프.
|
|
- **[Med] My_system.c (Air_Quality_color_process 선두 `return(0)`) — 본문 도달불가(데드코드)** (분석)
|
|
- 매초 호출되나 색상/quality 계산 전체가 실행 안 됨. 의도된 비활성화인지 확인 필요(색상은 Air_Quality_damper_process가 별도 세팅).
|
|
|
|
---
|
|
|
|
## 3. 모터 / 댐퍼 / PWM (MyMotor.c)
|
|
|
|
- **[Med] MyMotor.c:893·897 — 팬 PWM 보정항 정수 오버플로/truncation** (분석)
|
|
- `BLDC_SPEED_TABLE[..]*Volum_value/1000` 이 int로 먼저 계산 → 큰 값/음수 Volum에서 오버플로·정밀손실. 제안: float 우선 캐스팅 또는 Volum 범위 클램프.
|
|
- **[Med] MyMotor.c — `BLDC_SPEED_TABLE[Fan_Speed]`(크기100), `Target_Step_Count[damper_num]`(크기7) 인덱스 미검증** (분석)
|
|
- 정상 VSP/모드값에선 안전하나 방어 없음. Fan_Speed≥100 또는 damper_num≥7 시 OOB. 제안: 접근 전 클램프/가드.
|
|
- **[Med][의심] MyMotor.c:1011 외 — `Step_Status != 0x3F` 수렴 대기 무한블록 가능**
|
|
- 특정 타이밍에 6댐퍼 전부 0x3F 미도달 시 후속(팬 목표갱신) 진입 불가. 타임아웃/완료플래그 보강 검토.
|
|
- **[Low][의심] MyMotor.c:962-979 — `Vsp_Select` if-else 체인 공백구간**
|
|
- 10~15, 0x21~0x24 등 미정의 값은 어느 분기에도 안 걸려 Target/Damper 미설정. 정상 범위면 무해. default 처리 검토.
|
|
|
|
---
|
|
|
|
## 4. 제어 / EEPROM (MyControl.c)
|
|
|
|
- **[High] MyControl.c:595 — 필터리셋 조건 `Filter_timer_change` 중복(오타)** (분석)
|
|
- `if((Filter_timer_clean==0)&&(Filter_timer_change==0)&&(Filter_timer_change==0))` 세 번째는 `Soja_timer_change==0` 이어야 함 → 소자 청소/교체 카운터 리셋 누락. 제안: 세 번째를 `Soja_timer_change`로 교정.
|
|
- **[Med] MyControl.c:659 — `Pre_Mode_Control` 부분쓰기 루프 상한이 127** (분석)
|
|
- 정전복귀 3바이트(40~42)만 필요한데 `i<EEP_SIZE`(127)까지 순회 → 히스테리시스 영역(43~127)을 RAM 미러로 재기록. 동기화 타이밍에 따라 임계 롤백 위험 + 불필요 스캔. 제안: 상한을 42로 제한.
|
|
- **[Med][의심] MyControl.c:531·536 — `Volum1/2_value` 변환식이 주석범위(-100~100) 초과(+175~-75)**
|
|
- 다운스트림에서 -100~100 가정 시 오버레인지. 계수/오프셋 재검토 또는 결과 클램프.
|
|
|
|
---
|
|
|
|
## 5. Light_Bright 배열 불일치 (교차 이슈)
|
|
|
|
- **[Med] My_bunbaeggi.c:280 / My_define.h:495 — `Light_Bright[6]` 만 크기6** ✓
|
|
- 형제 각실배열(`Diffuser_*`, `SEN66_*` 등)은 모두 `[7]`(인덱스 0~6, 거실2=6). `Light_Bright`만 `[6]`(0~5). My_bunbaeggi.c:622/645 `Light_Bright[id_2]` 에서 id_2=6(거실2) 도달 시 OOB 읽기 → 인접 전역 오염. 제안: `Light_Bright[7]` 로 통일.
|
|
|
|
---
|
|
|
|
## 6. 점검했으나 "버그 아님" (오판 방지 기록)
|
|
|
|
- **MODE_* 중복정의(My_define.h 265-268 / 275-278)**: `#if((SPEC_MODE_INFO&0x0F)==0x03||==0x06)` 가드. 현재 `SPEC_MODE_INFO=0x16`(&0x0F=0x06) → **첫 블록만 컴파일**(VENT=0/AUTO=1/AIRCLEAN=2/BYPASS=3). 충돌 아님. 단 사양값 변경 시 BYPASS/AIRCLEAN(2↔3) 매핑이 바뀌는 잠재위험 → 프로토콜 코드값 고정 가정과 대조 필요.
|
|
- **CRC 계산범위/바이트순서(My_bunbaeggi, My_Homenet)**: 송수신 내부 일관(27B/37B 데이터 + 2B CRC). `CRC16()`이 표준MODBUS 바이트스왑값을 반환하고 `[n]=icrc>>8` 배치로 상쇄 → 와이어는 표준 리틀엔디안. 정상. (시뮬레이터는 lo-first로 일치시킴)
|
|
- **CTRL_RESERVE 곱셈(My_Homenet.c:385)**: `pl[0]*3600` int 승격으로 최대 28800 정상.
|
|
- **EEPROM 인덱스/페이지 경계(MyControl.c)**: 히스테리시스/임계 영역 최댓값 127(=EEP_SIZE-1), 페이지(128B) 침범 없음. 서명 0x55AA55AA + 별도 유효성 마커 검증 정상.
|
|
- **pwm_duty10000.c**: 룩업테이블이 아니라 표준 Nuvoton PWM 드라이버. 범위초과 없음.
|
|
|
|
---
|
|
|
|
## 참고
|
|
- 본 리포트는 분석만 수행했고 소스는 수정하지 않았습니다.
|
|
- (분석) 표기 항목은 영역별 분석에서 도출됐고 작성자 재확인 전입니다. 수정 착수 전 해당 라인을 함께 열어 확정하는 것을 권장합니다.
|
|
- 우선순위 1~4(High)는 동작 증상으로 이어질 가능성이 가장 높습니다.
|