9

Để bắt đầu, hãy để tôi nói rằng tôi hiểu cách thức và lý do vấn đề tôi mô tả có thể xảy ra. Tôi là một khoa học máy tính lớn, và tôi hiểu tràn/tràn và ký/unsigned số học. (Đối với những người không quen thuộc với chủ đề, Hướng dẫn Mã hóa An toàn của Apple discusses integer overflow một thời gian ngắn.)Cách tốt nhất để xử lý và báo cáo lỗi phân bổ bộ nhớ do tràn số nguyên trong Objective-C?

Câu hỏi của tôi là báo cáo và khôi phục từ một lỗi như vậy khi nó được phát hiện và cụ thể hơn trong trường hợp khung Mục tiêu-C. (Tôi viết và duy trì CHDataStructures.) Tôi có một vài lớp bộ sưu tập phân bổ bộ nhớ để lưu trữ các đối tượng và tự động mở rộng khi cần thiết. Tôi chưa thấy bất kỳ sự cố tràn liên quan đến tràn, có thể vì các trường hợp thử nghiệm của tôi chủ yếu sử dụng dữ liệu sane. Tuy nhiên, với các giá trị không được xác định, mọi thứ có thể phát nổ khá nhanh và tôi muốn ngăn chặn điều đó.

tôi đã xác định được ít nhất hai trường hợp phổ biến nơi này có thể xảy ra:

  1. Người gọi qua một giá trị rất lớn unsigned (hoặc giá trị ký âm) để -initWithCapacity:.
  2. Đủ đối tượng đã được thêm vào làm cho khả năng tự động mở rộng và khả năng phát triển đủ lớn để gây tràn.

Phần dễ dàng là phát hiện xem có xảy ra tràn hay không. (Ví dụ: trước khi cố gắng phân bổ length * sizeof(void*) byte, tôi có thể kiểm tra xem length <= UINT_MAX/sizeof(void*) hay không, vì việc thử nghiệm này sẽ có nghĩa là sản phẩm sẽ tràn và có khả năng phân bổ một vùng bộ nhớ nhỏ hơn nhiều so với mong muốn. thay thế.) Phần khó khăn hơn là xác định cách đối phó với nó một cách duyên dáng. Trong kịch bản đầu tiên, người gọi có lẽ được trang bị tốt hơn (hoặc ít nhất là trong tư duy) để đối phó với một thất bại. Kịch bản thứ hai có thể xảy ra ở bất kỳ đâu trong mã mà một đối tượng được thêm vào bộ sưu tập, có thể khá không xác định.

Câu hỏi của tôi, sau đó, là: Làm cách nào để "công dân tốt" Mã mục tiêu-C dự kiến ​​sẽ hoạt động khi số nguyên tràn xảy ra trong loại tình huống này? (Lý tưởng nhất, vì dự án của tôi là một khuôn khổ trong cùng tinh thần như Foundation trong Cocoa, tôi muốn mô hình hóa cách nó hoạt động cho tối đa "phù hợp trở kháng". Tài liệu của Apple tôi đã tìm thấy không đề cập đến nhiều ở tất cả về điều này.) Tôi thấy rằng trong mọi trường hợp, báo cáo lỗi là một lỗi nhất định. Vì các API để thêm một đối tượng (có thể gây ra kịch bản 2) không chấp nhận một tham số lỗi, tôi có thể thực sự làm gì để giúp giải quyết vấn đề, nếu có? Điều gì thực sự được coi là ổn trong những tình huống như vậy? Tôi rất lo lắng khi viết mã dễ bị lỗi nếu tôi có thể làm tốt hơn ...

Trả lời

3

Có hai vấn đề trong tầm tay:

(1) Phân bổ đã thất bại và bạn hết bộ nhớ.

(2) Bạn đã phát hiện tình trạng tràn hoặc điều kiện sai khác sẽ dẫn đến (1) nếu bạn tiếp tục.

Trong trường hợp (1), bạn được mời (trừ khi phân bổ không thành công vừa đủ lớn, ngu ngốc & bạn biết rằng phân bổ không thành công chỉ là một). Nếu điều này xảy ra, điều tốt nhất bạn có thể làm là đâm càng nhanh càng tốt và để lại nhiều bằng chứng nhất có thể. Đặc biệt, việc tạo một hàm gọi abort() của tên như IAmCrashingOnPurposeBecauseYourMemoryIsDepleted() sẽ để lại bằng chứng trong nhật ký sự cố.

Nếu thực sự là (2), thì có thêm câu hỏi. Cụ thể, bạn có thể phục hồi từ tình hình và, bất kể, dữ liệu của người dùng vẫn còn nguyên vẹn không? Nếu bạn có thể phục hồi, sau đó lớn ... làm như vậy và người dùng không bao giờ phải biết.Nếu không, thì bạn cần đảm bảo hoàn toàn dữ liệu của người dùng không bị hỏng. Nếu không, thì hãy cứu và chết đi. Nếu dữ liệu của người dùng bị hỏng, hãy cố gắng hết sức để không duy trì dữ liệu bị hỏng và cho người dùng biết rằng đã xảy ra sự cố nghiêm trọng. Nếu dữ liệu của người dùng đã tồn tại, nhưng bị hỏng, thì ... cũng ... ouch ... bạn có thể muốn xem xét việc tạo một công cụ phục hồi của một số loại.

+0

Hạnh phúc, vì mã được đề cập là khung bộ sưu tập cấp thấp, tôi thực sự không thể lo lắng cụ thể về bất kỳ dữ liệu người dùng nào, vì tôi không biết làm thế nào để tồn tại hoặc kiểm tra tham nhũng. Ở một số nơi mà (2) có thể xảy ra, tôi sẽ lập kế hoạch để làm một cái gì đó thông minh, như cố gắng phân bổ nhỏ hơn (nếu đó là một bộ sưu tập đang phát triển) hoặc trở về nil. Trong mọi trường hợp, tôi chắc chắn sẽ đăng nhập bằng chứng để giúp theo dõi vấn đề. Cảm ơn vì bối cảnh hữu ích cho việc đó. –

3

Đối với lưu trữ dựa trên mảng đang phát triển động, chỉ có quá nhiều thứ có thể thực hiện được. Tôi là nhà phát triển trên bộ lập lịch Moab cho siêu máy tính và chúng tôi cũng xử lý các con số rất lớn trên các hệ thống có hàng nghìn bộ vi xử lý, hàng nghìn công việc và lượng công việc lớn. Tại một thời điểm nào đó, bạn không thể khai báo một bộ đệm lớn hơn, mà không cần tạo một kiểu dữ liệu hoàn toàn mới để xử lý các kích thước lớn hơn UINT_MAX hoặc LONG_LONG_MAX v.v., tại thời điểm đó trên hầu hết các máy "bình thường" bạn sẽ chạy ra khỏi stack/heap không gian anyway. Vì vậy, tôi muốn ghi nhật ký một thông báo lỗi có ý nghĩa, giữ cho bộ sưu tập không phát nổ và nếu người dùng cần thêm nhiều thứ vào bộ sưu tập CHDataStructures, họ phải biết rằng có các vấn đề về số lượng rất lớn và người gọi nên kiểm tra xem liệu việc thêm có thành công hay không (theo dõi kích thước của bộ sưu tập, v.v.).

Khả năng khác là chuyển đổi bộ nhớ dựa trên mảng thành lưu trữ được phân bổ động, được liên kết dựa trên danh sách khi bạn nhận được điểm khi bạn không thể cấp phát mảng lớn hơn với một dấu int hoặc unsigned long. Điều này sẽ rất tốn kém, nhưng sẽ hiếm khi xảy ra mà nó không đáng chú ý đến người dùng khung. Vì giới hạn kích thước của bộ sưu tập dựa trên danh sách liên kết được phân bổ động, kích thước của heap, bất kỳ người dùng nào đã thêm đủ các mục vào bộ sưu tập để "tràn" nó sẽ có vấn đề lớn hơn có hay không thêm thành công.

4

Đăng nhập và tăng ngoại lệ. Bạn chỉ có thể thực sự là công dân tốt cho các lập trình viên khác, không phải người dùng cuối, vì vậy vượt qua vấn đề trên lầu và làm theo cách giải thích rõ ràng những gì đang xảy ra, vấn đề là gì (cung cấp số) và ở đâu nó đang xảy ra để nguyên nhân gốc rễ có thể được loại bỏ.

+0

Đây là câu trả lời hay, nhưng tôi chỉ có thể chọn một câu trả lời và câu trả lời của @ bbum có thêm chi tiết và ngữ cảnh hữu ích. –

1

Tôi muốn nói chính xác việc cần làm là làm những gì mà các bộ sưu tập Cocoa làm. Ví dụ: nếu tôi có mã sau:

int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    NSMutableArray * a = [[NSMutableArray alloc] init]; 

    for (uint32_t i = 0; i < ULONG_MAX; ++i) { 
     for (uint32_t i = 0; i < 10000000; ++i) { 
      [a addObject:@"foo"]; 
     } 
     NSLog(@"%lu rounds of 10,000,000 completed", i+1); 
    } 

    [a release]; 

    [pool drain]; 
    return 0; 
} 

..và chỉ để nó chạy, cuối cùng nó sẽ chết với EXC_BAD_ACCESS. (Tôi đã biên soạn và chạy ứng dụng này dưới dạng ứng dụng 32 bit để tôi có thể chắc chắn hết dung lượng khi tôi nhấn 2 ** 32 đối tượng.

Nói cách khác, ném ngoại lệ sẽ đẹp, nhưng tôi không ' t nghĩ rằng bạn thực sự cần phải làm gì cả.

0

Sử dụng khẳng định và một handler tùy chỉnh khẳng định có thể là lựa chọn tốt nhất dành cho bạn.

với khẳng định, bạn có thể dễ dàng có nhiều trạm kiểm soát trong mã của bạn, nơi bạn xác minh rằng Nếu chúng không, theo mặc định macro xác nhận sẽ ghi lại lỗi (chuỗi do nhà phát triển xác định) và ném một ngoại lệ. Bạn cũng có thể ghi đè hành vi mặc định bằng cách sử dụng trình xử lý xác nhận tùy chỉnh và thực hiện một cách để xử lý các điều kiện lỗi (thậm chí tránh ném ngoại lệ).

Cách tiếp cận này cho phép mức độ linh hoạt cao hơn và bạn có thể dễ dàng sửa đổi chiến lược xử lý lỗi của mình (ném ngoại lệ so với xử lý lỗi nội bộ) tại bất kỳ thời điểm nào.

Tài liệu rất súc tích: Assertions and Logging.