Segfault при вызове OpenProcessToken через Ruby/DL

Пожалуйста, смотрите обновление ниже

Я пытался исправить способность библиотеки Ruby взаимодействовать с другой программой в контексте UAC, и мне нужно создать общую карту файлов с теми же атрибутами безопасности, что и у текущего пользователя. Я использую Ruby/dl и пытаюсь заставить это работать на Ruby 1.9.3, и это привело к моей проблеме.

Вызов функции OpenProcessToken в advapi31 приводит к ошибке сегментации. Ниже вы найдете минимальный пример, который привел к ошибке сегментации на моей машине. Текст ошибки, которую я получил, находится здесь, а также снимок экрана с окном ошибки, которое появляется после того, как текст ошибки напечатан в командной строке:

Диалоговое окно с проблемой Ruby

require 'dl'
require 'dl/import'
require 'dl/types'

module Win
  extend DL::Importer

  dlload 'kernel32', 'advapi32'

  include DL::Win32Types

  # args: none
  # http://msdn.microsoft.com/en-us/library/windows/desktop/ms683179(v=vs.85).aspx
  extern 'HANDLE GetCurrentProcess()'

  # args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle
  # http://msdn.microsoft.com/en-us/library/windows/desktop/aa379295(v=vs.85).aspx
  extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)'

  # args: hObject
  # http://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
  extern 'BOOL CloseHandle(HANDLE)'

  # args: none
  # http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360(v=vs.85).aspx
  extern 'DWORD GetLastError()'

  def self.open_process_token
    token_handle = DL::CPtr.malloc(DL::SIZEOF_VOIDP, DL::RUBY_FREE)
    raise_error_if_zero(OpenProcessToken(Win.GetCurrentProcess, 0x8, token_handle.ref))
    raise_error_if_zero(CloseHandle(token_handle))
  end

  def self.raise_error_if_zero(result)
    if result == 0
      raise "Windows error: #{Win.GetLastError}"
    end
  end
end

Win.open_process_token

Обновить

Обновление Ruby до 1.9.3p545 (с использованием RubyInstaller) позволило мне запустить приведенный выше пример, но у меня продолжают возникать проблемы. Я создал здесь Gist, содержащий файлы, которые при запуске с 1.9.3p545 приводят к ошибке сегментации (хотя на этот раз переводчик перестает отвечать на запросы и выдает диалоговое окно, как указано выше). Я пробовал это (и ниже) на моем машина, а также другая с той же версией Ruby, установленной с тем же результатом. Поскольку я не упоминал об этом ранее, я использую 64-разрядную версию Windows 7 Pro, то же самое верно и для другого компьютера, на котором я его тестировал.

Я заметил несколько вещей, которые могут подразумевать более глубокую проблему, не обязательно связанную с OpenProcessToken. Любое из следующих действий может, по отдельности, предотвратить появление ошибки:

  • Копирование строки 3 из runner.rb в конец mwe.rb и прямой запуск mwe.rb.
  • Закомментирование строки 5 mwe.rb или комментирование некоторого большого подмножества errors.rb (например, комментирование строк с 37 по 99 не приводит к segfault).
  • Закомментирование строки 3 файла runner.rb, по сути, требует только других файлов и выхода.
  • Комментирование сочетания следующего изнутри Pageant::Win приводит к отсутствию ошибки:
    • Звонки в extern
    • Звонки в struct
    • Константы
    • Методы класса

В последнем случае нет необходимости комментировать все элементы определенной категории. Например, segfault можно избежать, если я закомментирую TOKEN_USER а также SECURITY_ATTRIBUTES, Я также могу предотвратить segfault, комментируя TOKEN_USER и extern заявление, связанное с IsValidSecurityDescriptor, Я пробовал несколько других комбинаций, которые приводят к тому же поведению.

Любая помощь будет оценена.

1 ответ

Решение

Эта ошибка связана не с ruby, а с вашим кодом.

Вы использовали неподходящий метод ref для переменной типа DL::CPtr в методе open_process_token.

Метод open_process_token

def self.open_process_token
  token_handle = DL::CPtr.malloc(DL::SIZEOF_VOIDP, DL::RUBY_FREE)
  OpenProcessToken(Win.GetCurrentProcess, 0x8, token_handle.ref)
end

Должно быть

def self.open_process_token
  ptoken_handle = DL::CPtr.malloc(DL::SIZEOF_VOIDP, DL::RUBY_FREE)
  OpenProcessToken(Win.GetCurrentProcess, 0x8, ptoken_handle)
  token_handle = ptoken_handle.ptr.to_i
end

(Спасибо CVertex за эту проблему.)

Другие вопросы по тегам