การปิดช่องโหว่ของเฟรมเวิร์กสื่อ

Android 7.0 ได้แยกกระบวนการแบบโมโนลิธิกmediaserverออกเป็นหลายกระบวนการที่มีสิทธิ์และความสามารถที่จำกัดไว้เฉพาะที่แต่ละกระบวนการต้องใช้ เพื่อปรับปรุงความปลอดภัยของอุปกรณ์ การเปลี่ยนแปลงเหล่านี้จะช่วยลดช่องโหว่ด้านความปลอดภัยของเฟรมเวิร์กสื่อด้วยวิธีต่อไปนี้

  • การแยกคอมโพเนนต์ไปป์ไลน์ AV ออกเป็นกระบวนการที่ใช้แซนด์บ็อกซ์เฉพาะแอป
  • การเปิดใช้คอมโพเนนต์สื่อที่อัปเดตได้ (โปรแกรมแยกไฟล์ โปรแกรมเปลี่ยนรหัส ฯลฯ)

การเปลี่ยนแปลงเหล่านี้ยังช่วยปรับปรุงความปลอดภัยให้กับผู้ใช้ปลายทางด้วย โดยลดความร้ายแรงของช่องโหว่ด้านความปลอดภัยที่เกี่ยวข้องกับสื่อส่วนใหญ่ได้อย่างมาก ซึ่งช่วยให้อุปกรณ์และข้อมูลของผู้ใช้ปลายทางปลอดภัย

OEM และผู้ให้บริการ SoC จำเป็นต้องอัปเดต HAL และการเปลี่ยนแปลงเฟรมเวิร์กเพื่อให้เข้ากันได้กับสถาปัตยกรรมใหม่ กล่าวโดยละเอียดคือ เนื่องจากโค้ด Android ที่ได้จากผู้ขายมักจะถือว่าทุกอย่างทำงานในกระบวนการเดียวกัน ผู้ขายจึงต้องอัปเดตโค้ดเพื่อส่งแฮนเดิลเนทีฟ (native_handle) ที่มีความหมายในกระบวนการต่างๆ ดูข้อมูลอ้างอิงสำหรับการติดตั้งใช้งานการเปลี่ยนแปลงที่เกี่ยวข้องกับการทำให้สื่อมีความปลอดภัยได้ที่ frameworks/av และ frameworks/native

การเปลี่ยนแปลงสถาปัตยกรรม

Android เวอร์ชันก่อนหน้าใช้mediaserverกระบวนการแบบโมโนลิธิคเพียงรายการเดียวที่มีสิทธิ์มากมาย (การเข้าถึงกล้อง การเข้าถึงเสียง การเข้าถึงไดรเวอร์วิดีโอ การเข้าถึงไฟล์ การเข้าถึงเครือข่าย ฯลฯ) Android 7.0 ได้แยกกระบวนการ mediaserver ออกเป็นกระบวนการใหม่หลายรายการที่แต่ละรายการต้องใช้ชุดสิทธิ์ที่น้อยลงมาก ดังนี้

การปิดช่องโหว่ของ Mediaserver

รูปที่ 1 การเปลี่ยนแปลงสถาปัตยกรรมเพื่อเพิ่มความแข็งแกร่งให้กับ Media Server

สถาปัตยกรรมใหม่นี้ช่วยให้มั่นใจได้ว่าแม้ว่ากระบวนการหนึ่งๆ จะถูกบุกรุก โค้ดที่เป็นอันตรายก็จะไม่มีสิทธิ์เข้าถึงชุดสิทธิ์ทั้งหมดที่ mediaserver มีก่อนหน้านี้ กระบวนการถูกจํากัดโดยนโยบาย SELinux และ seccomp

หมายเหตุ: เนื่องจากโค้ดบางอย่างต้องอาศัยผู้ให้บริการ โค้ดเหล่านี้จึงยังคงทำงานใน mediaserver และทำให้ mediaserver มีสิทธิ์มากกว่าที่จำเป็น กล่าวโดยละเอียดคือ Widevine Classic จะยังคงทำงานใน mediaserver สำหรับ Android 7.0

การเปลี่ยนแปลง MediaServer

ใน Android 7.0 กระบวนการ mediaserver มีไว้เพื่อขับเคลื่อนการเล่นและการบันทึก เช่น การส่งและซิงค์บัฟเฟอร์ระหว่างคอมโพเนนต์และกระบวนการ กระบวนการสื่อสารผ่านกลไก Binder แบบมาตรฐาน

ในเซสชันการเล่นไฟล์ในเครื่องแบบมาตรฐาน แอปจะส่งไฟล์อธิบาย (FD) ไปยัง mediaserver (โดยปกติผ่าน MediaPlayer Java API) และ mediaserver จะดำเนินการต่อไปนี้

  1. แพ็ก FD ไว้ในออบเจ็กต์ DataSource ของ Binder ที่ส่งไปยังกระบวนการแยกข้อมูล ซึ่งจะใช้เพื่ออ่านจากไฟล์โดยใช้ Binder IPC (mediaextractor ไม่ได้รับ FD แต่ทําให้ Binder โทรกลับหา mediaserver เพื่อรับข้อมูลแทน)
  2. ตรวจสอบไฟล์ สร้างเครื่องมือแยกที่เหมาะสมสำหรับประเภทไฟล์ (เช่น MP3Extractor หรือ MPEG4Extractor) และแสดงอินเทอร์เฟซ Binder สำหรับเครื่องมือแยกไปยังกระบวนการ mediaserver
  3. เรียก Binder IPC ไปยังเครื่องมือแยกข้อมูลเพื่อระบุประเภทข้อมูลในไฟล์ (เช่น ข้อมูล MP3 หรือ H.264)
  4. เรียกใช้กระบวนการ mediacodec เพื่อสร้างตัวแปลงรหัสประเภทที่ต้องการ รับอินเทอร์เฟซ Binder สำหรับตัวแปลงรหัสเหล่านี้
  5. เรียก Binder IPC ไปยังโปรแกรมแยกหลายครั้งเพื่ออ่านตัวอย่างที่เข้ารหัส ใช้ Binder IPC เพื่อส่งข้อมูลที่เข้ารหัสไปยังกระบวนการ mediacodec เพื่อถอดรหัส และรับข้อมูลที่ถอดรหัสแล้ว

ในบางกรณีจะไม่มีตัวแปลงรหัส (เช่น การเล่นที่ส่งออกข้อมูลไปยังอุปกรณ์เอาต์พุตโดยตรง) หรือตัวแปลงรหัสอาจแสดงผลข้อมูลที่ถอดรหัสโดยตรงแทนที่จะแสดงผลบัฟเฟอร์ของข้อมูลที่ถอดรหัส (การเล่นวิดีโอ)

การเปลี่ยนแปลง MediaCodecService

บริการโค้ดคือที่ที่โปรแกรมเปลี่ยนไฟล์และโปรแกรมถอดรหัสอาศัยอยู่ เนื่องจากความเกี่ยวเนื่องของผู้ให้บริการ จึงทำให้ตัวแปลงรหัสบางรายการยังไม่อยู่ในกระบวนการของตัวแปลงรหัส ใน Android 7.0 ให้ทำดังนี้

  • ตัวถอดรหัสที่ไม่ปลอดภัยและโปรแกรมเปลี่ยนไฟล์จะอยู่ในกระบวนการของโค้ดคิว
  • ตัวถอดรหัสที่ปลอดภัยและโปรแกรมเปลี่ยนไฟล์เป็นรหัสฮาร์ดแวร์อยู่ใน mediaserver (ไม่มีการเปลี่ยนแปลง)

แอป (หรือ mediaserver) จะเรียกใช้กระบวนการโค้ดเพื่อสร้างโค้ดประเภทที่ต้องการ จากนั้นเรียกใช้โค้ดนั้นเพื่อส่งข้อมูลที่เข้ารหัสและเรียกข้อมูลที่ถอดรหัสแล้ว (สำหรับการถอดรหัส) หรือส่งข้อมูลที่ถอดรหัสแล้วและเรียกข้อมูลที่เข้ารหัส (สำหรับการเข้ารหัส) การโอนข้อมูลจากและไปยังโปรแกรมเปลี่ยนรหัสใช้หน่วยความจำที่ใช้ร่วมกันอยู่แล้ว ดังนั้นกระบวนการดังกล่าวจึงไม่มีการเปลี่ยนแปลง

การเปลี่ยนแปลง MediaDrmServer

ระบบจะใช้เซิร์ฟเวอร์ DRM เมื่อเล่นเนื้อหาที่ได้รับการคุ้มครอง DRM เช่น ภาพยนตร์ใน Google Play Movies ซึ่งจะจัดการการถอดรหัสข้อมูลที่เข้ารหัสด้วยวิธีที่ปลอดภัย และด้วยเหตุนี้จึงมีสิทธิ์เข้าถึงใบรับรองและที่เก็บคีย์ รวมถึงคอมโพเนนต์ที่ละเอียดอ่อนอื่นๆ กระบวนการ DRM ยังไม่พร้อมใช้งานในบางกรณีเนื่องจากความซับซ้อนของผู้ให้บริการ

การเปลี่ยนแปลง AudioServer

กระบวนการ AudioServer โฮสต์คอมโพเนนต์ที่เกี่ยวข้องกับเสียง เช่น อินพุตและเอาต์พุตเสียง บริการตัวจัดการนโยบายที่กำหนดการกำหนดเส้นทางเสียง และบริการวิทยุ FM โปรดดูรายละเอียดเกี่ยวกับการเปลี่ยนแปลงเสียงและคำแนะนำการใช้งานที่หัวข้อใช้เสียง

การเปลี่ยนแปลง CameraServer

CameraServer จะควบคุมกล้องและใช้เมื่อบันทึกวิดีโอเพื่อรับเฟรมวิดีโอจากกล้อง จากนั้นส่งไปยัง mediaserver เพื่อดำเนินการต่อ ดูรายละเอียดเกี่ยวกับการเปลี่ยนแปลงและคำแนะนำการใช้งานสำหรับการเปลี่ยนแปลงของ CameraServer ได้ที่การเพิ่มความปลอดภัยให้กับเฟรมเวิร์กกล้อง

การเปลี่ยนแปลง ExtractorService

บริการเครื่องมือแยกข้อมูลจะโฮสต์เครื่องมือแยกข้อมูล ซึ่งเป็นคอมโพเนนต์ที่แยกวิเคราะห์รูปแบบไฟล์ต่างๆ ที่เฟรมเวิร์กสื่อรองรับ บริการแยกไฟล์มีสิทธิ์น้อยที่สุดเมื่อเทียบกับบริการอื่นๆ เนื่องจากไม่สามารถอ่าน FD ได้ แต่จะเรียกใช้อินเทอร์เฟซ Binder (mediaserver forเซสชันการเล่นแต่ละเซสชัน) เพื่อเข้าถึงไฟล์แทน

แอป (หรือ mediaserver) จะเรียกใช้กระบวนการแยกไฟล์เพื่อรับ IMediaExtractor แล้วเรียกใช้ IMediaExtractor นั้นเพื่อรับ IMediaSources สำหรับแทร็กที่อยู่ในไฟล์ จากนั้นเรียกใช้ IMediaSources เพื่ออ่านข้อมูลจาก IMediaExtractor

หากต้องการโอนข้อมูลระหว่างกระบวนการ แอป (หรือ mediaserver) จะรวมข้อมูลไว้ในแพ็กเกจตอบกลับเป็นส่วนหนึ่งของธุรกรรม Binder หรือใช้หน่วยความจำที่ใช้ร่วมกัน

  • การใช้หน่วยความจําที่ใช้ร่วมกันต้องเรียก Binder เพิ่มเติมเพื่อปลดปล่อยหน่วยความจําที่ใช้ร่วมกัน แต่จะใช้พลังงานน้อยลงและเร็วขึ้นสําหรับบัฟเฟอร์ขนาดใหญ่
  • การใช้ in-Parcel ต้องมีการคัดลอกเพิ่มเติม แต่จะใช้พลังงานน้อยลงและเร็วกว่าสำหรับบัฟเฟอร์ที่มีขนาดเล็กกว่า 64 KB

การใช้งาน

หากต้องการรองรับการย้ายคอมโพเนนต์ MediaDrm และ MediaCrypto ไปยังกระบวนการ mediadrmserver ใหม่ ผู้ให้บริการต้องเปลี่ยนวิธีการจัดสรรบัฟเฟอร์ที่ปลอดภัยเพื่อให้แชร์บัฟเฟอร์ระหว่างกระบวนการได้

ใน Android เวอร์ชันก่อนหน้า ระบบจะจัดสรรบัฟเฟอร์ที่ปลอดภัยใน mediaserver โดย OMX::allocateBuffer และใช้ระหว่างการถอดรหัสในกระบวนการเดียวกัน ดังที่แสดงด้านล่าง

รูปที่ 2 การจัดสรรบัฟเฟอร์ใน MediaServer ของ Android 6.0 และต่ำกว่า

ใน Android 7.0 กระบวนการจัดสรรบัฟเฟอร์ได้เปลี่ยนไปใช้กลไกใหม่ซึ่งมีความยืดหยุ่นมากขึ้น ในขณะเดียวกันก็ลดผลกระทบต่อการใช้งานที่มีอยู่ เมื่อใช้กอง MediaDrm และ MediaCrypto ในกระบวนการ mediadrmserver ใหม่ ระบบจะจัดสรรบัฟเฟอร์ในลักษณะที่แตกต่างออกไป และผู้ขายต้องอัปเดตแฮนเดิลบัฟเฟอร์ที่ปลอดภัยเพื่อให้สามารถส่งผ่าน Binder ได้เมื่อ MediaCodec เรียกใช้การดำเนินการถอดรหัสใน MediaCrypto

รูปที่ 3 การจัดสรรบัฟเฟอร์ใน MediaServer ของ Android 7.0 ขึ้นไป

ใช้แฮนเดิลเนทีฟ

OMX::allocateBuffer ต้องแสดงผลพอยน์เตอร์ไปยังโครงสร้าง native_handle ซึ่งมีตัวระบุไฟล์ (FD) และข้อมูลจำนวนเต็มเพิ่มเติม native_handle มีข้อดีทั้งหมดของการใช้ FD รวมถึงการรองรับ Binder ที่มีอยู่สำหรับการแปลงเป็นอนุกรม/การแปลงอนุกรม และให้ความยืดหยุ่นมากขึ้นแก่ผู้ให้บริการที่ไม่ได้ใช้ FD ในปัจจุบัน

ใช้ native_handle_create() เพื่อจัดสรรแฮนเดิลเนทีฟ โค้ดเฟรมเวิร์กจะเป็นเจ้าของโครงสร้าง native_handle ที่สร้างขึ้นและมีหน้าที่รับผิดชอบในการปล่อยทรัพยากรทั้งในระหว่างกระบวนการที่สร้างขึ้น native_handle เป็นครั้งแรกและในระหว่างกระบวนการที่แปลงข้อมูล native_handle กลับ เฟรมเวิร์กจะปล่อยแฮนเดิลเนทีฟที่มี native_handle_close() ตามด้วย native_handle_delete() และจัดรูปแบบ/แยกรูปแบบ native_handle โดยใช้ Parcel::writeNativeHandle()/readNativeHandle()

ผู้ให้บริการ SoC ที่ใช้ FD เพื่อแสดงบัฟเฟอร์ที่ปลอดภัยจะป้อนข้อมูล FD ในnative_handle ด้วย FD ของตนได้ ผู้ให้บริการที่ไม่ได้ใช้ FD สามารถแสดงบัฟเฟอร์ที่ปลอดภัยได้โดยใช้ช่องเพิ่มเติมใน native_buffer

ตั้งค่าตำแหน่งการถอดรหัส

ผู้ให้บริการต้องอัปเดตวิธีการถอดรหัส OEMCrypto ที่ทำงานกับ native_handle เพื่อดำเนินการเฉพาะของผู้ให้บริการที่จำเป็นเพื่อให้ native_handle ใช้งานได้ในพื้นที่กระบวนการใหม่ (โดยปกติแล้วการเปลี่ยนแปลงจะรวมถึงการอัปเดตไลบรารี OEMCrypto)

เนื่องจาก allocateBuffer เป็นการดำเนินการ OMX มาตรฐาน Android 7.0 จึงมีส่วนขยาย OMX ใหม่ (OMX.google.android.index.allocateNativeHandle) เพื่อค้นหาการรองรับนี้และการเรียก OMX_SetParameter ที่แจ้งให้การใช้งาน OMX ทราบว่าควรใช้แฮนเดิลแบบเนทีฟ