# 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_bunbaegi.c:869 | High | 예약시간 offset 오류: `[8]` 검사하고 값은 `[7]`(풍량)에서 읽음 | ✓ | | 4 | MyControl.c:595 | High | 필터리셋 조건에 `Filter_timer_change` 중복(`Soja_timer_change` 오타) → 소자 리셋 누락 | (분석) | | 5 | My_bunbaegi.c:708·898 | Med | 수신 id(`buffer[3]`) 범위검증 없이 크기7 배열 인덱싱 → OOB 쓰기 가능 | (분석) | | 6 | My_bunbaegi.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_bunbaegi.c (분배기/디퓨저 마스터) - **[High] My_bunbaegi.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_bunbaegi.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_bunbaegi.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_bunbaegi.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>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)는 동작 증상으로 이어질 가능성이 가장 높습니다.