본문 바로가기
C.W.K.
Stream
Lesson 05 of 05 · published

손대기 직전에 다시 확인해

~12 min · revalidation, toctou, apply-time

Level 0Finder 에서 헤매는 중
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"네가 본 건 기억이고, 네가 바꾸려는 건 지금이야. 둘을 헷갈리면 파일이 사라져."

보는 순간과 하는 순간 사이

모든 파일 작업엔 틈이 있어. 화면이 파일을 보여준 순간과 실제로 손대는 순간 사이. 1초일 수도 있고, 메뉴를 열어둔 채 한 시간이 갈 수도 있어. 그사이 세상은 움직여. 파일 이름이 바뀌거나 덮어써지거나 지워지고, 볼륨이 빠졌다 다시 붙고, 권한이 달라지고, 목적지 폴더가 꽉 차거나 사라지고, 없던 충돌이 생기지. 보안 하는 사람들은 이런 부류를 확인한 때와 쓰는 때가 다른 문제라고 불러. 부주의한 파일 도구가 데이터를 잃는 자리가 여기야.

일찍 말고 늦게 확인해

해법은 언제 확인하느냐의 규율이야. Waygate 는 줄을 그릴 때 참이던 걸 믿지 않아. 손대기 직전에, 엔진이 디스크를 건드리기 바로 전에, 참조를 다시 풀고 전제를 통째로 확인해. 경로가 풀리는지, 그 파일이 맞는지, 볼륨이 같은지, 권한이 아직 되는지, 원본 속성이 그대로인지, 목적지가 준비됐는지, 충돌 상태가 계획대로인지. 하나라도 어긋나면 바이트 한 톨 움직이기 전에 멈춰.

이게 트랙을 묶어주는 이유

이 트랙 전체가 여기서 값을 치러. 파일시스템이 진짜니까 다시 확인하면 진짜를 읽게 되고, 경로가 신분증이 아니니까 존재가 아니라 신분을 대조하고, 새로고침이 폴더를 다시 읽으니까 화면이 정직하고, DB 가 수첩이니까 믿을 낡은 목록이 없어. 손대기 직전의 확인은 이 네 가지를 한꺼번에 쓰는 순간이야. 마지막 검사를 기억이 아니라 현실에 대고 할 수 있도록 앞의 규칙들이 존재하는 거지.

화면에 그릴 때가 아니라 손대기 직전에 확인해. 경로도, 파일 신분도, 볼륨도, 권한도, 원본 속성도, 목적지도, 충돌 상태도 줄을 그린 뒤에 얼마든지 달라져. 파일을 바꾸기 전 마지막 관문은 계획이 가정한 것과 지금 파일시스템을 맞춰보고, 하나라도 다르면 멈추는 거야.
난 검증이란 게 앞에서 하는 일인 줄 알았어. 입력 확인하고, 그다음 실행. Waygate 를 만들면서 반대를 배웠어. 여럿이 같이 쓰는 걸 건드릴 땐 손대기 바로 전에 하는 확인만 진짜야. 그 앞의 검사들은 사용자한테 베푸는 친절이고(버튼을 흐리게 해준다든가), 진짜 관문은 늦고 볼품없는 자리에 있어. 그런데 파일을 살리는 건 그쪽이야.

Code

마지막 관문: 계획이 가정한 걸 지금 다시 확인·swift
func gateBeforeMutation(_ plan: OperationPlan) throws {
    let src = try revalidate(plan.source)            // still the same file?
    let srcVals = try src.resourceValues(forKeys: [
        .contentModificationDateKey, .fileSizeKey])
    guard srcVals matches plan.expectedSourceMetadata
        else { throw OpError.sourceChanged }         // renamed/edited under us
    guard try volumeStillMounted(plan.source.volumeID)
        else { throw OpError.volumeGone }            // SMB dropped, USB pulled
    guard try destinationReady(plan.destination)
        else { throw OpError.destinationUnavailable }
    let collisionNow = try detectCollision(plan.destination)
    guard collisionNow == plan.assumedCollision
        else { throw OpError.collisionChanged }      // occupant appeared/changed
    // Only past ALL of these does the engine touch a single byte.
}
그 틈에서 벌어지는 일·text
  display time -------- (the gap) -------- apply time
  |                                        |
  row drawn:                               user hits Move
  report.pdf, 2.1 MB,                       <-- Waygate re-checks HERE
  modified 09:14                           is it still that file?
                                           same volume? same size/date?
     meanwhile, in the gap:                destination still ok?
     - file edited to 5.0 MB                collision unchanged?
     - or replaced entirely                any 'no' -> STOP, don't guess

External links

Exercise

아무 앱에서나 되돌릴 수 없는 동작을 하나 골라봐. 휴지통 비우기, 저장하면서 덮어쓰기, 선택 삭제 같은 거. 확인과 실행 사이의 틈을 짚어봐. 뭘 언제 확인하고, 실행까지 얼마나 걸려? 그 틈에서 세상이 바뀌어 엉뚱한 결과가 나오는 상황을 하나 지어보고, 늦게 하는 검사 하나로 어떻게 막히는지 말해봐.
Hint
고전은 '저장' 이야. 파일을 연 뒤에 디스크에서 그 파일이 바뀌었는데 그대로 덮어쓰는 거지. 열 때 확인하고 저장할 때 쓰니까 사이가 비어. 널 살리는 건 쓰기 직전에 지금 수정 상태를 가정과 맞춰보고 다르면 멈추는 검사야. 확인한 때가 아니라 쓰는 때에 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.