Skip to content

[AI Refactoring] IME GetSurroundingText/GetSelectedText 네이티브 문자열 미해제 (키 입력 핫 패스 누수) + UpdatePreEditString 성공 경로 attr/GList 미해제 [Scope: src/Tizen.Uix.InputMethod] (2026-08-16) #7803

Description

@JoonghyunCho

[Type: Refactoring]
[Scope: src/Tizen.Uix.InputMethod]
[Priority: 🔴 Critical]
[Lens: Performance, Coding Guidelines]

Observation

InputMethodEditor의 네이티브 문자열/구조체 메모리 해제가 세 곳에서 누락되어 있습니다. 이 모듈의 Interop에는 free() 바인딩(Interop.Libc)이 아예 존재하지 않아, 네이티브가 caller-owned로 반환하는 메모리를 해제할 수단 자체가 없습니다.

1) GetSurroundingText (InputMethodEditor.cs:1841-1851)

public static void GetSurroundingText(int maxLenBefore, int maxLenAfter, out string text, out int cursorPosition)
{
    IntPtr txt;
    ErrorCode error = ImeGetSurroundingText(maxLenBefore, maxLenAfter, out txt, out cursorPosition);
    ...
    text = Marshal.PtrToStringAnsi(txt);   // txt 를 free() 하지 않음
}

네이티브 ime_get_surrounding_text()는 text를 호출자가 free()로 해제해야 하는 계약입니다(Tizen native API 문서 remarks). 현재 관리 문자열로 복사만 하고 원본 버퍼를 방치합니다.

2) GetSelectedText (InputMethodEditor.cs:2078-2088) — ime_get_selected_text() 동일 계약, 동일 누수.

3) UpdatePreEditString (InputMethodEditor.cs:1727-1775) — 성공 경로 미해제:

try
{
    foreach (PreEditAttribute attribute in attrs)
    {
        IntPtr attr = Marshal.AllocHGlobal(Marshal.SizeOf(imePreEditAttribute)); // 16B/attr
        allocatedAttrs.Add(attr);
        ...
        glist = Interop.GLib.GListAppend(glist, attr);                           // +GList 노드/attr
    }
    ErrorCode error = ImeUpdatePreeditStringWithGlist(str, glist);
    if (error != ErrorCode.None) { ... throw ...; }
    // 성공 시: allocatedAttrs / glist 해제 없이 반환 → 전량 누수
}
catch
{
    // 실패 경로에서만 FreeHGlobal + GListFree 수행
    throw;
}

catch 경로에서는 해제하면서 성공 경로에서는 해제하지 않는 비대칭 구조입니다. 네이티브가 소유권을 가져간다면 catch 경로의 해제가 double-free이고, 가져가지 않는다면(네이티브는 호출 중 IPC 직렬화 후 리스트를 소유하지 않음) 성공 경로가 누수입니다 — 두 경로 중 하나는 반드시 잘못되어 있으며, 네이티브 계약상 성공 경로 누수입니다.

Problem

  • Performance 렌즈: IME는 서라운딩 텍스트를 커서 이동/키 입력마다 조회하고, 프리에딧은 조합 중 매 키 입력마다 갱신되는 핫 패스입니다. 호출당 수십 바이트씩 프로세스 수명 내내 무한 누적되는 네이티브 힙 누수입니다. IME는 상시 구동 프로세스라 누적 효과가 특히 큽니다.
  • Coding Guidelines 렌즈: 네이티브 out-string 계약(caller frees) 위반. TizenFX 타 모듈(예: Tizen.Network.Smartcard의 Interop.Libc.Free)이 따르는 표준 패턴 미적용. 성공/실패 경로의 자원 관리 비대칭은 try/finally로 정상화해야 합니다.

Proposed Improvement

1. Interop/Interop.Libc.cs 신규 추가 (Smartcard 등 기존 모듈과 동일 패턴):

internal static partial class Interop
{
    internal static partial class Libc
    {
        [DllImport(Libraries.Libc, EntryPoint = "free")]
        internal static extern void Free(IntPtr ptr);
    }
}

2. 문자열 반환 2곳 해제 (After — GetSurroundingText 예):

IntPtr txt = IntPtr.Zero;
ErrorCode error = ImeGetSurroundingText(maxLenBefore, maxLenAfter, out txt, out cursorPosition);
if (error != ErrorCode.None) { ... throw ...; }
try
{
    text = Marshal.PtrToStringAnsi(txt);
}
finally
{
    if (txt != IntPtr.Zero)
        Interop.Libc.Free(txt);
}

GetSelectedText도 동일 적용.

3. UpdatePreEditString — catch 해제를 finally로 이동:

try
{
    ... alloc + GListAppend ...
    ErrorCode error = ImeUpdatePreeditStringWithGlist(str, glist);
    if (error != ErrorCode.None) { ... throw ...; }
}
finally
{
    foreach (IntPtr attr in allocatedAttrs)
        Marshal.FreeHGlobal(attr);
    if (glist != IntPtr.Zero)
        Interop.GLib.GListFree(glist);
}

Target Files

  • src/Tizen.Uix.InputMethod/Tizen.Uix.InputMethod/InputMethodEditor.cs
  • src/Tizen.Uix.InputMethod/Interop/Interop.Libc.cs (신규)
  • src/Tizen.Uix.InputMethod/Interop/Interop.Libraries.cs (Libc 상수 추가)

Expected Impact (Quantitative Metrics)

  • GetSurroundingText: 호출당 strlen(text)+1 바이트 누수 → 0. 예: 평균 64B 컨텍스트, 분당 200회 입력 시 ~12.8KB/분 누적이 제거됨.
  • GetSelectedText: 호출당 선택 텍스트 길이+1 바이트 누수 → 0.
  • UpdatePreEditString: 조합 중 키 입력당 attr 1개 기준 40B(구조체 16B + GList 노드 ~24B) 누수 → 0.
  • 상시 구동 IME 프로세스의 네이티브 힙 무한 증가 요인 3곳 제거.

API Compatibility Check

  • Public API signature change: 없음 (메서드 본문 내부 자원 해제만 추가)
  • Behavior change: 없음 (반환값/예외 동일; 누수만 제거)
  • Tizen API Level floor: 유지 (신규 .NET API 미사용, libc free 바인딩은 플랫폼 공통)

Impact Scope

  • 수정 심볼 호출부: 모듈 내부 3개 메서드 본문 한정 (rg 기준 저장소 내 참조는 본 모듈 8곳 + Tizen.NUI의 동명 이종 클래스 — NUI 측은 무관)
  • 다른 어셈블리 영향: 없음
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions