পাঠ ২৫ · ৫৭-এর মধ্যে · মডিউল ৬
Home / Courses / Ethics in Computing & AI Safety / কেস স্টাডি: Therac-25

কেস স্টাডি: Therac-25

Case study: the Therac-25 radiation therapy accidents
১১ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • Therac-25-এ ঠিক কী ঘটেছিল -- সংক্ষিপ্ত, নথিভুক্ত তথ্যের ভিত্তিতে
  • রেস কন্ডিশন নামক বাগের মূল কারণ, এবং কেন হার্ডওয়্যার ইন্টারলক সরিয়ে ফেলা এটিকে মারাত্মক করে তুলেছিল
  • কেন সমস্যাটি দ্রুত ধরা পড়েনি -- পরীক্ষার ঘাটতি ও প্রস্তুতকারকের প্রতিক্রিয়ায় বিলম্ব
  • Therac-25 কীভাবে সেফটি-ক্রিটিক্যাল সফটওয়্যার ইঞ্জিনিয়ারিং চর্চাকে স্থায়ীভাবে বদলে দিয়েছে
  • Python দিয়ে "রেস কন্ডিশন" নামক বাগের ধরনটি ব্যাখ্যা করার একটি ন্যূনতম, শিক্ষামূলক মডেল

১ · কী ঘটেছিল

Therac-25Therac-25AECL-এর তৈরি একটি কম্পিউটার-নিয়ন্ত্রিত রেডিয়েশন থেরাপি মেশিন, ১৯৮০-এর দশকের মাঝামাঝি সময়ে মোতায়েন করা হয়। ছিল একটি কম্পিউটার-নিয়ন্ত্রিত রেডিয়েশন থেরাপি মেশিন, নির্মাতা প্রতিষ্ঠান AECL, যা ১৯৮০-এর দশকের মাঝামাঝি সময়ে উত্তর আমেরিকার বেশ কয়েকটি ক্লিনিকে স্থাপন করা হয়েছিল। মেশিনটির কাজ ছিল ক্যান্সার রোগীদের নির্দিষ্ট, নিয়ন্ত্রিত মাত্রায় রেডিয়েশন প্রদান করা -- একটি সঠিক ডোজ যা টিউমারকে লক্ষ্য করে অথচ আশেপাশের সুস্থ টিস্যুকে যতটা সম্ভব সুরক্ষিত রাখে।

১৯৮৫ থেকে ১৯৮৭ সালের মধ্যে, অন্তত ছয়টি জ্ঞাত দুর্ঘটনায় Therac-25 রোগীদের উদ্দিষ্ট মাত্রার চেয়ে বহুগুণ বেশি রেডিয়েশন প্রদান করে। বেশ কয়েকজন রোগী মারা যান বা গুরুতর, স্থায়ী শারীরিক ক্ষতির শিকার হন। এই ঘটনাগুলোর সুনির্দিষ্ট মৃত্যুর সংখ্যা নিয়ে বিভিন্ন উৎসে ভিন্নতা আছে, তাই এই পাঠ কোনো একক নির্দিষ্ট সংখ্যা দাবি করবে না -- গুরুত্বপূর্ণ তথ্যটি হলো: একাধিক রোগী প্রকৃত, গুরুতর ক্ষতির শিকার হয়েছিলেন, এবং কয়েকজন মারা যান।

২ · মূল কারণ: একটি সফটওয়্যার রেস কন্ডিশন

মূল প্রযুক্তিগত কারণ ছিল একটি রেস কন্ডিশনRace Conditionযখন একাধিক অপারেশনের ফলাফল তাদের সম্পন্ন হওয়ার আপেক্ষিক সময়ের (টাইমিং) উপর নির্ভর করে, এবং একটি নির্দিষ্ট টাইমিং-ক্রমে একটি নিরাপত্তা-চেক এড়িয়ে যাওয়া সম্ভব হয়। -- যদি একজন অপারেটর নির্দিষ্ট গতিতে ও নির্দিষ্ট ক্রমে চিকিৎসার প্যারামিটার প্রবেশ করান বা সম্পাদনা করেন, তাহলে একটি নিরাপত্তা-চেক এড়িয়ে যাওয়া সম্ভব হতো। আগের মডেলগুলোর বিপরীতে, Therac-25 হার্ডওয়্যার ইন্টারলক (একটি স্বাধীন, হার্ডওয়্যারভিত্তিক নিরাপত্তা-লক যা সফটওয়্যারের সিদ্ধান্তের উপর নির্ভর করে না) সরিয়ে ফেলে কেবলমাত্র সফটওয়্যার সেফটি চেকের উপর নির্ভর করেছিল। ফলে যখন সেই সফটওয়্যার চেক টাইমিং বাগের কারণে ব্যর্থ হয়, তখন কোনো স্বাধীন হার্ডওয়্যার সুরক্ষা-স্তর অবশিষ্ট ছিল না যা বিপর্যয় ঠেকাতে পারতো।

আগের মডেল সফটওয়্যার চেক + হার্ডওয়্যার ইন্টারলক (দুই স্তর) Therac-25 শুধুমাত্র সফটওয়্যার চেক (এক স্তর) সফটওয়্যার চেক টাইমিং বাগে ব্যর্থ হলে কোনো স্বতন্ত্র সুরক্ষা-স্তর অবশিষ্ট নেই
দুটি স্বতন্ত্র নিরাপত্তা-স্তর থাকলে একটির ব্যর্থতা অন্যটি ঠেকাতে পারতো; একক স্তরে নির্ভরতা মানেই সেই একটি স্তরের যেকোনো ব্যর্থতা সরাসরি বিপর্যয়ে রূপান্তরিত হয়।

৩ · কেন এটি দ্রুত ধরা পড়েনি

দুটি কারণ একসাথে সমস্যাটিকে দীর্ঘস্থায়ী করে তুলেছিল। প্রথমত, এই ধরনের টাইমিং-নির্ভর, কনকারেন্ট (concurrent/সমান্তরাল) অপারেটর-ইনপুট সংমিশ্রণ পরীক্ষা করা কঠিন -- নির্দিষ্ট গতিতে নির্দিষ্ট ক্রমে ইনপুট না দিলে বাগটি প্রকাশ পেতো না, এবং প্রি-রিলিজ টেস্টিং এই ধরনের বিরল, টাইমিং-নির্ভর ইনপুট-সিকোয়েন্স যথেষ্ট পরিমাণে পরীক্ষা করেনি। দ্বিতীয়ত, প্রাথমিক দুর্ঘটনার রিপোর্ট পাওয়ার পর প্রস্তুতকারকের প্রতিক্রিয়া ধীর ও অপর্যাপ্ত ছিল -- সমস্যাটির প্রকৃত মূল কারণ (রেস কন্ডিশন) দ্রুত শনাক্ত ও সমাধান করা হয়নি, ফলে একই ধরনের দুর্ঘটনা একাধিকবার পুনরাবৃত্তি হয়।

লক্ষণীয়

এখানে গুরুত্বপূর্ণ শিক্ষা হলো -- একটি বিচ্ছিন্ন ঘটনার রিপোর্ট পাওয়ার পর তাকে গুরুত্বসহকারে তদন্ত করা এবং দ্রুত প্রতিক্রিয়া জানানো নিজেই একটি নৈতিক দায়িত্ব, বিশেষত সেফটি-ক্রিটিক্যাল সিস্টেমে। প্রথম দুর্ঘটনার পরেই মূল কারণ পুঙ্খানুপুঙ্খভাবে অনুসন্ধান করা হলে পরবর্তী দুর্ঘটনাগুলো এড়ানো যেত।

৪ · সেফটি-ক্রিটিক্যাল সফটওয়্যার প্র্যাকটিসে স্থায়ী প্রভাব

Therac-25 আজও সফটওয়্যার ইঞ্জিনিয়ারিং সেফটি ও এথিক্সের সবচেয়ে বেশি উদ্ধৃত কেস স্টাডিগুলোর একটি। এটি নিম্নলিখিত নীতিগুলোকে সেফটি-ক্রিটিক্যাল সিস্টেম ডিজাইনে দৃঢ়ভাবে প্রতিষ্ঠিত করতে সাহায্য করেছে -- রিডানডেন্ট (একাধিক, স্বতন্ত্র) নিরাপত্তা-স্তর রাখা, যাতে একটি স্তর ব্যর্থ হলেও অন্যটি রক্ষা করতে পারে; কনকারেন্ট/টাইমিং-নির্ভর আচরণ বিশেষভাবে কঠোরভাবে পরীক্ষা করা; এবং প্রতিটি অস্বাভাবিক ঘটনা বা এরর-রিপোর্টকে সম্ভাব্য গুরুতর সংকেত হিসেবে দ্রুত ও পুঙ্খানুপুঙ্খভাবে তদন্ত করা। L28-এ আমরা দেখব কেন টেস্টিং একাই কখনো "সম্পূর্ণ নিশ্চয়তা" দিতে পারে না -- Therac-25 এই সীমাবদ্ধতার একটি বাস্তব, মর্মান্তিক উদাহরণ।

৫ · রেস কন্ডিশনের ধরন -- একটি শিক্ষামূলক ইলাস্ট্রেশন

নিচের কোড Therac-25-এর প্রকৃত সোর্স কোড নয় -- এই কোর্সের কাছে সেই কোড নেই, এবং এখানে তা পুনর্নির্মাণের কোনো দাবিও করা হচ্ছে না। বরং এটি একটি ন্যূনতম, সরলীকৃত মডেল যা রেস কন্ডিশন নামক বাগের ধরনটি -- কীভাবে একটি "স্টেল" (পুরনো, আর বৈধ নয় এমন) সেফটি-চেক ফলাফল ব্যবহারের কারণে একটি নিরাপত্তা-চেক কার্যকরভাবে এড়িয়ে যাওয়া সম্ভব হতে পারে -- বোঝার জন্য তৈরি করা হয়েছে।

Python
# শিক্ষামূলক ইলাস্ট্রেশন -- Therac-25-এর প্রকৃত সোর্স কোড নয় (যা এই কোর্সের কাছে নেই এবং
# পুনর্নির্মাণের দাবিও করা হচ্ছে না)। এটি শুধু "রেস কন্ডিশন" নামক বাগের ধরনটি একটি
# ন্যূনতম, সরলীকৃত মডেলে দেখানোর জন্য।

class TreatmentSession:
    def __init__(self):
        self.mode = "স্ট্যান্ডার্ড"  # সেশন সবসময় "স্ট্যান্ডার্ড" মোডে শুরু হয়

def switch_mode(session, new_mode):
    session.mode = new_mode

def safety_check(session):
    # সেফটি চেক শুধুমাত্র "স্ট্যান্ডার্ড" মোডে বিম চালুর অনুমতি দেয়
    return session.mode == "স্ট্যান্ডার্ড"

def commit_beam(check_result):
    return "বিম চালু (beam fired)" if check_result else "বিম বন্ধ (beam blocked)"

def run_safe_interleaving():
    """নিরাপদ ক্রম: কমিটের ঠিক আগ মুহূর্তে সেফটি চেক (পুনরায়) করা হয় -- সবসময় সর্বশেষ অবস্থা দেখেই"""
    session = TreatmentSession()
    switch_mode(session, "হাই-ইনটেনসিটি")   # অপারেটর দ্রুত মোড পরিবর্তন করলেন
    check_result = safety_check(session)     # কমিটের ঠিক আগে চেক -- বর্তমান, আপ-টু-ডেট অবস্থা দেখেই
    return commit_beam(check_result), check_result

def run_race_condition_interleaving():
    """বিপজ্জনক ক্রম: সেফটি চেক আগেই করে ফলাফল সংরক্ষণ করা হয়, তারপর অপারেটর দ্রুত মোড পরিবর্তন করেন --
    কিন্তু কমিট সেই পুরনো (stale) চেক-ফলাফল ব্যবহার করে, নতুন অবস্থার সাথে আর পুনরায় মেলানো হয় না"""
    session = TreatmentSession()
    stale_check_result = safety_check(session)  # তখনো মোড "স্ট্যান্ডার্ড" -- চেক পাস করে (True)
    switch_mode(session, "হাই-ইনটেনসিটি")        # অপারেটর দ্রুত মোড পরিবর্তন করলেন, কিন্তু আর চেক হয়নি
    return commit_beam(stale_check_result), stale_check_result

safe_outcome, safe_check = run_safe_interleaving()
race_outcome, race_check = run_race_condition_interleaving()

print(f"নিরাপদ ইন্টারলিভিং      : সেফটি-চেক ফলাফল={safe_check}, চূড়ান্ত ফলাফল={safe_outcome}")
print(f"রেস-কন্ডিশন ইন্টারলিভিং : সেফটি-চেক ফলাফল={race_check}, চূড়ান্ত ফলাফল={race_outcome}")

    
লক্ষ্য করুন -- দুটি ফাংশনই একই দুটি কাজ (মোড পরিবর্তন, সেফটি চেক) করে, শুধু ক্রম ভিন্ন। নিরাপদ ক্রমে চেকটি কমিটের ঠিক আগে, সর্বশেষ অবস্থার উপর করা হয় -- তাই মোড পরিবর্তনের পরের, প্রকৃত অবস্থা সঠিকভাবে ধরা পড়ে এবং বিম বন্ধ থাকে। রেস-কন্ডিশন ক্রমে চেকটি আগে করে ফলাফল সংরক্ষণ করা হয়, তারপর অবস্থা বদলে যায় -- কিন্তু কমিট সেই পুরনো ফলাফলই ব্যবহার করে, ফলে বিম ভুলভাবে চালু হয়ে যায়। এই ধরনের "স্টেল চেক-রেজাল্ট" সমস্যাই রেস কন্ডিশন-জনিত বাগের একটি সাধারণ, প্রকৃত ধরন।
মূল কথা · Key takeaway

Therac-25 দেখায় কীভাবে একটি সফটওয়্যার টাইমিং বাগ, স্বতন্ত্র হার্ডওয়্যার সুরক্ষার অনুপস্থিতিতে, সরাসরি অপরিবর্তনীয় মানবিক ক্ষতির কারণ হতে পারে। এটি একটি সতর্কীকরণ যে সফটওয়্যারের উপর একমাত্র নির্ভরতা, এমনকি সাবধানে লেখা কোডেও, যথেষ্ট নয় যখন ঝুঁকি এতটা বেশি -- রিডানডেন্সি ও দ্রুত, গুরুত্বসহকারে ঘটনা-তদন্ত উভয়ই অপরিহার্য।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন -- তারপর "→ উত্তর" চাপুন।

প্র ০১ হার্ডওয়্যার ইন্টারলক সরিয়ে ফেলার সিদ্ধান্তটি কেন এত গুরুত্বপূর্ণ ছিল?

কারণ এটি একটি স্বতন্ত্র, সফটওয়্যারের সিদ্ধান্তের উপর নির্ভরশীল নয় এমন সুরক্ষা-স্তর সরিয়ে দেয়। যতক্ষণ দুটি স্বাধীন সুরক্ষা-স্তর থাকে, ততক্ষণ একটির ব্যর্থতা অন্যটি ঠেকাতে পারে। একটি মাত্র স্তরে (সফটওয়্যারে) নির্ভর করা মানে সেই একক স্তরের যেকোনো বাগ সরাসরি, বাধাহীনভাবে বাস্তব বিপর্যয়ে রূপান্তরিত হতে পারে।

প্র ০২ কেন রেস কন্ডিশনের মতো টাইমিং-নির্ভর বাগ সাধারণ টেস্টিংয়ে ধরা পড়া কঠিন?

কারণ বাগটি প্রকাশ পাওয়ার জন্য একটি নির্দিষ্ট, সম্ভবত বিরল সময়-ক্রম প্রয়োজন হয় -- স্বাভাবিক গতিতে বা ভিন্ন ক্রমে ইনপুট দিলে সিস্টেম সঠিকভাবে কাজ করে, তাই পরীক্ষকরা সহজেই ধরে নিতে পারেন সবকিছু ঠিক আছে। শুধুমাত্র নির্দিষ্ট, দ্রুত ইনপুট-সিকোয়েন্স পরীক্ষা করলেই বাগটি ধরা পড়তো -- যা প্রচলিত ফাংশনাল টেস্টিংয়ে প্রায়ই মিস হয়ে যায়।

প্র ০৩ উপরের কোড সেলে run_race_condition_interleaving-এ যদি switch_mode কলটির পরপরই সেফটি চেক আবার করা হতো (কমিটের ঠিক আগে), তাহলে কী হতো?

তাহলে safety_check(session) নতুন, আপ-টু-ডেট অবস্থার উপর (মোড ইতিমধ্যে "হাই-ইনটেনসিটি") পুনরায় মূল্যায়িত হতো এবং False ফেরত দিতো -- ঠিক run_safe_interleaving-এর মতো, বিম বন্ধ থাকতো। এটিই দেখায় যে সমাধানটি জটিল নয়: কমিটের ঠিক আগে সর্বশেষ অবস্থার উপর পুনরায় চেক করাই যথেষ্ট -- সমস্যাটি ছিল কখন চেক করা হচ্ছে তার ডিজাইন-সিদ্ধান্তে, চেকের যুক্তিতে নয়।

অনুশীলন

  1. চিন্তা করুন: Therac-25-এর ঘটনায় প্রস্তুতকারকের প্রাথমিক দুর্ঘটনার প্রতি ধীর প্রতিক্রিয়া কেন নিজেই একটি নৈতিক ব্যর্থতা হিসেবে বিবেচিত হয়, শুধু প্রযুক্তিগত ব্যর্থতা নয়?

    কারণ প্রযুক্তিগত বাগ ঘটা এক জিনিস, আর একটি গুরুতর ঘটনার রিপোর্ট পাওয়ার পরেও দ্রুত ও পুঙ্খানুপুঙ্খভাবে তদন্ত না করা সম্পূর্ণ ভিন্ন একটি সিদ্ধান্ত -- যা ভবিষ্যতের সম্ভাব্য ক্ষতিকে গুরুত্ব না দেওয়ার সমতুল্য। সেফটি-ক্রিটিক্যাল সিস্টেমে একটি প্রাথমিক সতর্কীকরণ সংকেতকে গুরুত্ব না দেওয়া নিজেই একটি স্বতন্ত্র, এড়ানো-সম্ভব নৈতিক ব্যর্থতা।

  2. পরীক্ষা করুন: উপরের কোড সেলে একটি তৃতীয় ফাংশন run_double_switch_interleaving() লিখুন যেখানে অপারেটর মোড দুইবার পরিবর্তন করেন (স্ট্যান্ডার্ড → হাই-ইনটেনসিটি → স্ট্যান্ডার্ড) স্টেল চেকের পরে, কমিটের আগে -- ফলাফল কী হয় দেখুন।

    যদি ফাংশনটি চেকের পর দুইবার মোড পরিবর্তন করে শেষে "স্ট্যান্ডার্ড"-এ ফিরিয়ে আনে, তাহলে চূড়ান্ত প্রকৃত অবস্থা আসলে নিরাপদ (স্ট্যান্ডার্ড), কিন্তু স্টেল চেক-ফলাফল ব্যবহার করলে তা কাকতালীয়ভাবে সঠিক উত্তর দিতে পারে -- যা আরও বিপজ্জনক শিক্ষা দেয়: স্টেল-চেক ব্যবহারকারী কোড কখনো কখনো "ভাগ্যক্রমে" সঠিক ফলাফল দিতে পারে, যা পরীক্ষার সময় বাগটিকে আরও লুকিয়ে রাখে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
সেফটি-ক্রিটিক্যাল সিস্টেম ও বাগের প্রকৃত মূল্য