راهنمای معاملات

معرفی

هنگام استفاده از یک کتابخانه C/C++ unsandbox، پیوند دهنده اطمینان حاصل می کند که همه عملکردهای لازم پس از کامپایل در دسترس هستند و بنابراین نیازی به نگرانی نیست که آیا تماس API ممکن است در زمان اجرا با شکست مواجه شود.

با این حال، هنگام استفاده از کتابخانه Sandboxed، اجرای کتابخانه در یک فرآیند جداگانه زندگی می کند. شکست در تماس API مستلزم بررسی انواع مشکلات مربوط به ارسال تماس از لایه RPC است. گاهی اوقات، خطاهای لایه RPC ممکن است مورد توجه نباشند، به عنوان مثال هنگام انجام پردازش انبوه و جعبه سند به تازگی راه اندازی مجدد شده است.

با این وجود، به دلایلی که در بالا ذکر شد، مهم است که بررسی خطای منظم مقدار بازگشتی فراخوانی API sandboxed را گسترش دهید تا بررسی شود که آیا خطایی در لایه RPC برگردانده شده است یا خیر. به همین دلیل است که همه نمونه‌های اولیه تابع کتابخانه به جای T ::sapi::StatusOr<T> را برمی‌گردانند. در صورتی که فراخوانی تابع کتابخانه ناموفق باشد (مثلاً به دلیل نقض جعبه سند)، مقدار بازگشتی حاوی جزئیات مربوط به خطای رخ داده خواهد بود. .

رسیدگی به خطاهای لایه RPC به این معنی است که هر تماس با یک کتابخانه Sandboxed توسط یک بررسی اضافی از لایه RPC SAPI دنبال می‌شود. به منظور مقابله با آن موقعیت‌های استثنایی، SAPI ماژول SAPI Transaction ( transaction.h ) را ارائه می‌کند. این ماژول شامل کلاس ::sapi::Transaction است و اطمینان می‌دهد که همه فراخوانی‌های تابع به کتابخانه Sandboxed بدون هیچ مشکلی در سطح RPC تکمیل شده‌اند یا خطای مربوطه را برمی‌گردانند.

تراکنش SAPI

SAPI کد میزبان را از کتابخانه Sandboxed جدا می کند و به تماس گیرنده این امکان را می دهد که درخواست پردازش داده مشکل ساز را راه اندازی مجدد یا لغو کند. SAPI Transaction یک قدم جلوتر می رود و به طور خودکار فرآیندهای شکست خورده را تکرار می کند.

تراکنش‌های SAPI را می‌توان به دو روش مختلف استفاده کرد: یا به‌طور مستقیم از ::sapi::Transaction به ارث می‌برند، یا با استفاده از نشانگرهای تابع ارسال شده به ::sapi::BasicTransaction .

تراکنش های SAPI با نادیده گرفتن سه تابع زیر تعریف می شوند:

روش های معامله SAPI
::sapi::Transaction::Init() این شبیه به فراخوانی روش اولیه سازی یک کتابخانه معمولی C/C++ است. این روش فقط یک بار در طول هر تراکنش به کتابخانه Sandboxed فراخوانی می شود، مگر اینکه تراکنش مجدداً راه اندازی شود. در مورد راه اندازی مجدد، صرف نظر از اینکه قبلا چند راه اندازی مجدد اتفاق افتاده است، متد دوباره فراخوانی می شود.
::sapi::Transaction::Main() این متد برای هر فراخوانی به ::sapi::Transaction::Run() فراخوانی می شود.
::sapi::Transaction::Finish() این شبیه به فراخوانی روش پاکسازی یک کتابخانه معمولی C/C++ است. این روش تنها یک بار در حین تخریب شیء SAPI Transaction فراخوانی می شود.

استفاده عادی از کتابخانه

در پروژه‌ای بدون کتابخانه‌های sandboxed، الگوی معمول هنگام برخورد با کتابخانه‌ها چیزی شبیه به این است:

LibInit();
while (data = NextDataToProcess()) {
  result += LibProcessData(data);
}
LibClose();

کتابخانه مقداردهی اولیه می شود، سپس از توابع صادر شده کتابخانه استفاده می شود و در نهایت یک تابع end/close برای پاکسازی محیط فراخوانی می شود.

استفاده از کتابخانه Sandboxed

در پروژه‌ای با کتابخانه‌های sandboxed، هنگام استفاده از تراکنش‌های دارای پاسخ تماس، کد از Normal Library Use به قطعه کد زیر ترجمه می‌شود:

// Overwrite the Init method, passed to BasicTransaction initialization
::absl::Status Init(::sapi::Sandbox* sandbox) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Instantiate the Sandboxed Library
  SAPI_RETURN_IF_ERROR(lib.LibInit());
  return ::absl::OkStatus();
}

// Overwrite the Finish method, passed to BasicTransaction initialization
::absl::Status Finish(::sapi::Sandbox *sandbox) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Clean-up sandboxed library instance
  SAPI_RETURN_IF_ERROR(lib.LibClose());
  return ::absl::OkStatus();
}

// Wrapper function to process data, passed to Run method call
::absl::Status HandleData(::sapi::Sandbox *sandbox, Data data_to_process,
                           Result *out) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Call the sandboxed function LibProcessData
  SAPI_ASSIGN_OR_RETURN(*out, lib.LibProcessData(data_to_process));
  return ::absl::OkStatus();
}

void Handle() {
  // Use SAPI Transactions by passing function pointers to ::sapi::BasicTransaction
  ::sapi::BasicTransaction transaction(Init, Finish);
  while (data = NextDataToProcess()) {
    ::sandbox2::Result result;
    // call the ::sapi::Transaction::Run() method
    transaction.Run(HandleData, data, &result);
    // ...
  }
  // ...
}

کلاس تراکنش مطمئن می شود که کتابخانه را مجدداً راه اندازی می کند در صورتی که خطایی در طول فراخوانی handle_data رخ دهد - در بخش زیر در مورد این موضوع بیشتر توضیح دهید.

تراکنش دوباره شروع می شود

اگر یک فراخوانی API کتابخانه sandboxed خطایی در طول اجرای روش‌های تراکنش SAPI ایجاد کند (جدول بالا را ببینید)، تراکنش مجدداً راه‌اندازی می‌شود. تعداد پیش‌فرض راه‌اندازی مجدد توسط kDefaultRetryCnt در transaction.h تعریف می‌شود.

نمونه هایی از خطاهای مطرح شده که باعث راه اندازی مجدد می شوند عبارتند از:

  • نقض جعبه ایمنی رخ داد
  • فرآیند سندباکس خراب شد
  • یک تابع sandbox شده به دلیل یک خطای کتابخانه کد خطا را برگرداند

رویه راه‌اندازی مجدد جریان Init() و Main() عادی را مشاهده می‌کند، و اگر فراخوانی‌های مکرر به متد ::sapi::Transaction::Run() خطا را برمی‌گرداند، سپس کل متد یک خطا را به فراخوان‌کننده‌اش برمی‌گرداند.

Sandbox یا RPC Error Handling

رابط Sandboxed Library که به صورت خودکار تولید می‌شود، تلاش می‌کند تا حد امکان به نمونه اولیه تابع کتابخانه اصلی C/C++ نزدیک باشد. با این حال، Sandboxed Library باید بتواند هر گونه خطای sandbox یا RPC را علامت دهد.

این امر با استفاده از انواع بازگشتی ::sapi::StatusOr<T> (یا ::sapi::Status برای توابعی که void را برمی‌گردانند)، به‌جای بازگرداندن مستقیم مقدار بازگشتی توابع sandbox شده به دست می‌آید.

SAPI برخی ماکروهای راحت را برای بررسی و واکنش به یک شی وضعیت SAPI ارائه می دهد. این ماکروها در فایل هدر status_macro.h تعریف شده اند.

قطعه کد زیر گزیده ای از مثال جمع است و استفاده از وضعیت SAPI و ماکروها را نشان می دهد:

// Instead of void, use ::sapi::Status
::sapi::Status SumTransaction::Main() {
  // Instantiate the SAPI Object
  SumApi f(sandbox());

  // ::sapi::StatusOr<int> sum(int a, int b)
  SAPI_ASSIGN_OR_RETURN(int v, f.sum(1000, 337));
  // ...

  // ::sapi::Status sums(sapi::v::Ptr* params)
  SumParams params;
  params.mutable_data()->a = 1111;
  params.mutable_data()->b = 222;
  params.mutable_data()->ret = 0;
  SAPI_RETURN_IF_ERROR(f.sums(params.PtrBoth()));
  // ...
  // Gets symbol address and prints its value
  int *ssaddr;
  SAPI_RETURN_IF_ERROR(sandbox()->Symbol(
      "sumsymbol", reinterpret_cast<void**>(&ssaddr)));
  ::sapi::v::Int sumsymbol;
  sumsymbol.SetRemote(ssaddr);
  SAPI_RETURN_IF_ERROR(sandbox()->TransferFromSandboxee(&sumsymbol));
  // ...
  return ::sapi::OkStatus();
}

Sandbox دوباره راه اندازی می شود

بسیاری از کتابخانه های sandbox شده ورودی حساس کاربر را مدیریت می کنند. اگر کتابخانه sandboxed در نقطه‌ای خراب شود و داده‌ها را بین اجراها ذخیره کند، این داده‌های حساس در خطر هستند. برای مثال، اگر یک نسخه sandbox شده از کتابخانه Imagemagick شروع به ارسال تصاویر اجرای قبلی کند.

برای جلوگیری از چنین سناریویی، سندباکس نباید برای اجراهای متعدد مورد استفاده مجدد قرار گیرد. برای متوقف کردن استفاده مجدد از جعبه‌های ایمنی، کد میزبان می‌تواند با استفاده از ::sapi::Sandbox::Restart() یا ::sapi::Transaction::Restart() یک راه‌اندازی مجدد فرآیند کتابخانه sandbox شده را هنگام استفاده از تراکنش‌های SAPI آغاز کند.

راه‌اندازی مجدد هرگونه ارجاع به فرآیند کتابخانه sandbox را باطل می‌کند. این بدان معنی است که توصیف کننده های فایل ارسال شده یا حافظه اختصاص داده شده دیگر وجود نخواهد داشت.

،

معرفی

هنگام استفاده از یک کتابخانه C/C++ unsandbox، پیوند دهنده اطمینان حاصل می کند که همه عملکردهای لازم پس از کامپایل در دسترس هستند و بنابراین نیازی به نگرانی نیست که آیا تماس API ممکن است در زمان اجرا با شکست مواجه شود.

با این حال، هنگام استفاده از کتابخانه Sandboxed، اجرای کتابخانه در یک فرآیند جداگانه زندگی می کند. شکست در تماس API مستلزم بررسی انواع مشکلات مربوط به ارسال تماس از لایه RPC است. گاهی اوقات، خطاهای لایه RPC ممکن است مورد توجه نباشند، به عنوان مثال هنگام انجام پردازش انبوه و جعبه سند به تازگی راه اندازی مجدد شده است.

با این وجود، به دلایلی که در بالا ذکر شد، مهم است که بررسی خطای منظم مقدار بازگشتی فراخوانی API sandboxed را گسترش دهید تا بررسی شود که آیا خطایی در لایه RPC برگردانده شده است یا خیر. به همین دلیل است که همه نمونه‌های اولیه تابع کتابخانه به جای T ::sapi::StatusOr<T> را برمی‌گردانند. در صورتی که فراخوانی تابع کتابخانه ناموفق باشد (مثلاً به دلیل نقض جعبه سند)، مقدار بازگشتی حاوی جزئیات مربوط به خطای رخ داده خواهد بود. .

رسیدگی به خطاهای لایه RPC به این معنی است که هر تماس با یک کتابخانه Sandboxed توسط یک بررسی اضافی از لایه RPC SAPI دنبال می‌شود. به منظور مقابله با آن موقعیت‌های استثنایی، SAPI ماژول SAPI Transaction ( transaction.h ) را ارائه می‌کند. این ماژول شامل کلاس ::sapi::Transaction است و اطمینان می‌دهد که همه فراخوانی‌های تابع به کتابخانه Sandboxed بدون هیچ مشکلی در سطح RPC تکمیل شده‌اند یا خطای مربوطه را برمی‌گردانند.

تراکنش SAPI

SAPI کد میزبان را از کتابخانه Sandboxed جدا می کند و به تماس گیرنده این امکان را می دهد که درخواست پردازش داده مشکل ساز را راه اندازی مجدد یا لغو کند. SAPI Transaction یک قدم جلوتر می رود و به طور خودکار فرآیندهای شکست خورده را تکرار می کند.

تراکنش‌های SAPI را می‌توان به دو روش مختلف استفاده کرد: یا به‌طور مستقیم از ::sapi::Transaction به ارث می‌برند، یا با استفاده از نشانگرهای تابع ارسال شده به ::sapi::BasicTransaction .

تراکنش های SAPI با نادیده گرفتن سه تابع زیر تعریف می شوند:

روش های معامله SAPI
::sapi::Transaction::Init() این شبیه به فراخوانی روش اولیه سازی یک کتابخانه معمولی C/C++ است. این روش فقط یک بار در طول هر تراکنش به کتابخانه Sandboxed فراخوانی می شود، مگر اینکه تراکنش مجدداً راه اندازی شود. در مورد راه اندازی مجدد، صرف نظر از اینکه قبلا چند راه اندازی مجدد اتفاق افتاده است، متد دوباره فراخوانی می شود.
::sapi::Transaction::Main() این متد برای هر فراخوانی به ::sapi::Transaction::Run() فراخوانی می شود.
::sapi::Transaction::Finish() این شبیه به فراخوانی روش پاکسازی یک کتابخانه معمولی C/C++ است. این روش تنها یک بار در حین تخریب شیء SAPI Transaction فراخوانی می شود.

استفاده عادی از کتابخانه

در پروژه‌ای بدون کتابخانه‌های sandboxed، الگوی معمول هنگام برخورد با کتابخانه‌ها چیزی شبیه به این است:

LibInit();
while (data = NextDataToProcess()) {
  result += LibProcessData(data);
}
LibClose();

کتابخانه مقداردهی اولیه می شود، سپس از توابع صادر شده کتابخانه استفاده می شود و در نهایت یک تابع end/close برای پاکسازی محیط فراخوانی می شود.

استفاده از کتابخانه Sandboxed

در پروژه‌ای با کتابخانه‌های sandboxed، هنگام استفاده از تراکنش‌های دارای پاسخ تماس، کد از Normal Library Use به قطعه کد زیر ترجمه می‌شود:

// Overwrite the Init method, passed to BasicTransaction initialization
::absl::Status Init(::sapi::Sandbox* sandbox) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Instantiate the Sandboxed Library
  SAPI_RETURN_IF_ERROR(lib.LibInit());
  return ::absl::OkStatus();
}

// Overwrite the Finish method, passed to BasicTransaction initialization
::absl::Status Finish(::sapi::Sandbox *sandbox) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Clean-up sandboxed library instance
  SAPI_RETURN_IF_ERROR(lib.LibClose());
  return ::absl::OkStatus();
}

// Wrapper function to process data, passed to Run method call
::absl::Status HandleData(::sapi::Sandbox *sandbox, Data data_to_process,
                           Result *out) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Call the sandboxed function LibProcessData
  SAPI_ASSIGN_OR_RETURN(*out, lib.LibProcessData(data_to_process));
  return ::absl::OkStatus();
}

void Handle() {
  // Use SAPI Transactions by passing function pointers to ::sapi::BasicTransaction
  ::sapi::BasicTransaction transaction(Init, Finish);
  while (data = NextDataToProcess()) {
    ::sandbox2::Result result;
    // call the ::sapi::Transaction::Run() method
    transaction.Run(HandleData, data, &result);
    // ...
  }
  // ...
}

کلاس تراکنش مطمئن می شود که کتابخانه را مجدداً راه اندازی می کند در صورتی که خطایی در طول فراخوانی handle_data رخ دهد - در بخش زیر در مورد این موضوع بیشتر توضیح دهید.

تراکنش دوباره شروع می شود

اگر یک فراخوانی API کتابخانه sandboxed خطایی در طول اجرای روش‌های تراکنش SAPI ایجاد کند (جدول بالا را ببینید)، تراکنش مجدداً راه‌اندازی می‌شود. تعداد پیش‌فرض راه‌اندازی مجدد توسط kDefaultRetryCnt در transaction.h تعریف می‌شود.

نمونه هایی از خطاهای مطرح شده که باعث راه اندازی مجدد می شوند عبارتند از:

  • نقض جعبه ایمنی رخ داد
  • فرآیند سندباکس خراب شد
  • یک تابع sandbox شده به دلیل یک خطای کتابخانه کد خطا را برگرداند

رویه راه‌اندازی مجدد جریان Init() و Main() عادی را مشاهده می‌کند، و اگر فراخوانی‌های مکرر به متد ::sapi::Transaction::Run() خطا را برمی‌گرداند، سپس کل متد یک خطا را به فراخوان‌کننده‌اش برمی‌گرداند.

Sandbox یا RPC Error Handling

رابط Sandboxed Library که به صورت خودکار تولید می‌شود، تلاش می‌کند تا حد امکان به نمونه اولیه تابع کتابخانه اصلی C/C++ نزدیک باشد. با این حال، Sandboxed Library باید بتواند هر گونه خطای sandbox یا RPC را علامت دهد.

این امر با استفاده از انواع بازگشتی ::sapi::StatusOr<T> (یا ::sapi::Status برای توابعی که void را برمی‌گردانند)، به‌جای بازگرداندن مستقیم مقدار بازگشتی توابع sandbox شده به دست می‌آید.

SAPI برخی ماکروهای راحت را برای بررسی و واکنش به یک شی وضعیت SAPI ارائه می دهد. این ماکروها در فایل هدر status_macro.h تعریف شده اند.

قطعه کد زیر گزیده ای از مثال جمع است و استفاده از وضعیت SAPI و ماکروها را نشان می دهد:

// Instead of void, use ::sapi::Status
::sapi::Status SumTransaction::Main() {
  // Instantiate the SAPI Object
  SumApi f(sandbox());

  // ::sapi::StatusOr<int> sum(int a, int b)
  SAPI_ASSIGN_OR_RETURN(int v, f.sum(1000, 337));
  // ...

  // ::sapi::Status sums(sapi::v::Ptr* params)
  SumParams params;
  params.mutable_data()->a = 1111;
  params.mutable_data()->b = 222;
  params.mutable_data()->ret = 0;
  SAPI_RETURN_IF_ERROR(f.sums(params.PtrBoth()));
  // ...
  // Gets symbol address and prints its value
  int *ssaddr;
  SAPI_RETURN_IF_ERROR(sandbox()->Symbol(
      "sumsymbol", reinterpret_cast<void**>(&ssaddr)));
  ::sapi::v::Int sumsymbol;
  sumsymbol.SetRemote(ssaddr);
  SAPI_RETURN_IF_ERROR(sandbox()->TransferFromSandboxee(&sumsymbol));
  // ...
  return ::sapi::OkStatus();
}

Sandbox دوباره راه اندازی می شود

بسیاری از کتابخانه های sandbox شده ورودی حساس کاربر را مدیریت می کنند. اگر کتابخانه sandboxed در نقطه‌ای خراب شود و داده‌ها را بین اجراها ذخیره کند، این داده‌های حساس در خطر هستند. برای مثال، اگر یک نسخه sandbox شده از کتابخانه Imagemagick شروع به ارسال تصاویر اجرای قبلی کند.

برای جلوگیری از چنین سناریویی، سندباکس نباید برای اجراهای متعدد مورد استفاده مجدد قرار گیرد. برای متوقف کردن استفاده مجدد از جعبه‌های ایمنی، کد میزبان می‌تواند با استفاده از ::sapi::Sandbox::Restart() یا ::sapi::Transaction::Restart() یک راه‌اندازی مجدد فرآیند کتابخانه sandbox شده را هنگام استفاده از تراکنش‌های SAPI آغاز کند.

راه‌اندازی مجدد هرگونه ارجاع به فرآیند کتابخانه sandbox را باطل می‌کند. این بدان معنی است که توصیف کننده های فایل ارسال شده یا حافظه اختصاص داده شده دیگر وجود نخواهد داشت.