Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(776)

Side by Side Diff: media/filters/skcanvas_video_renderer.cc

Issue 624633002: Fix potential use-after-free bug in VideoImageGenerator::onGetYUV8Planes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/filters/skcanvas_video_renderer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/skcanvas_video_renderer.h" 5 #include "media/filters/skcanvas_video_renderer.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "media/base/video_frame.h" 8 #include "media/base/video_frame.h"
9 #include "media/base/yuv_convert.h" 9 #include "media/base/yuv_convert.h"
10 #include "third_party/libyuv/include/libyuv.h" 10 #include "third_party/libyuv/include/libyuv.h"
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 } 201 }
202 default: 202 default:
203 NOTREACHED(); 203 NOTREACHED();
204 break; 204 break;
205 } 205 }
206 } 206 }
207 207
208 // Generates an RGB image from a VideoFrame. 208 // Generates an RGB image from a VideoFrame.
209 class VideoImageGenerator : public SkImageGenerator { 209 class VideoImageGenerator : public SkImageGenerator {
210 public: 210 public:
211 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) {} 211 VideoImageGenerator(const scoped_refptr<VideoFrame>& frame) : frame_(frame) {
212 DCHECK(frame_.get());
213 }
212 virtual ~VideoImageGenerator() {} 214 virtual ~VideoImageGenerator() {}
213 215 void discard_frame() {
214 void set_frame(const scoped_refptr<VideoFrame>& frame) { frame_ = frame; } 216 frame_ = NULL;
217 }
215 218
216 protected: 219 protected:
217 virtual bool onGetInfo(SkImageInfo* info) OVERRIDE { 220 virtual bool onGetInfo(SkImageInfo* info) OVERRIDE {
218 info->fWidth = frame_->visible_rect().width(); 221 info->fWidth = frame_->visible_rect().width();
219 info->fHeight = frame_->visible_rect().height(); 222 info->fHeight = frame_->visible_rect().height();
220 info->fColorType = kN32_SkColorType; 223 info->fColorType = kN32_SkColorType;
221 info->fAlphaType = kPremul_SkAlphaType; 224 info->fAlphaType = kPremul_SkAlphaType;
222 return true; 225 return true;
223 } 226 }
224 227
225 virtual bool onGetPixels(const SkImageInfo& info, 228 virtual bool onGetPixels(const SkImageInfo& info,
226 void* pixels, 229 void* pixels,
227 size_t row_bytes, 230 size_t row_bytes,
228 SkPMColor ctable[], 231 SkPMColor ctable[],
229 int* ctable_count) OVERRIDE { 232 int* ctable_count) OVERRIDE {
230 if (!frame_.get()) 233 // This method should be called only one time.
234 DCHECK(frame_.get());
235 if (!pixels)
231 return false; 236 return false;
232 if (!pixels)
233 return true;
234 // If skia couldn't do the YUV conversion, we will. 237 // If skia couldn't do the YUV conversion, we will.
235 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes); 238 ConvertVideoFrameToRGBPixels(frame_, pixels, row_bytes);
236 frame_ = NULL;
237 return true; 239 return true;
238 } 240 }
239 241
240 virtual bool onGetYUV8Planes(SkISize sizes[3], 242 virtual bool onGetYUV8Planes(SkISize sizes[3],
241 void* planes[3], 243 void* planes[3],
242 size_t row_bytes[3], 244 size_t row_bytes[3],
243 SkYUVColorSpace* color_space) OVERRIDE { 245 SkYUVColorSpace* color_space) OVERRIDE {
244 if (!frame_.get() || !IsYUV(frame_->format())) 246 if (!frame_.get() || !IsYUV(frame_->format()))
245 return false; 247 return false;
248 #if DCHECK_IS_ON
249 // This method should be called to get a YUV plane only one time.
250 if (row_bytes && planes)
251 DCHECK(frame_.get());
252 #endif
246 253
247 if (color_space) { 254 if (color_space) {
248 if (IsJPEGColorSpace(frame_->format())) 255 if (IsJPEGColorSpace(frame_->format()))
249 *color_space = kJPEG_SkYUVColorSpace; 256 *color_space = kJPEG_SkYUVColorSpace;
250 else 257 else
251 *color_space = kRec601_SkYUVColorSpace; 258 *color_space = kRec601_SkYUVColorSpace;
252 } 259 }
253 260
254 for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane; 261 for (int plane = VideoFrame::kYPlane; plane <= VideoFrame::kVPlane;
255 ++plane) { 262 ++plane) {
(...skipping 12 matching lines...) Expand all
268 if (plane == media::VideoFrame::kYPlane) { 275 if (plane == media::VideoFrame::kYPlane) {
269 offset = (frame_->stride(media::VideoFrame::kYPlane) * 276 offset = (frame_->stride(media::VideoFrame::kYPlane) *
270 frame_->visible_rect().y()) + 277 frame_->visible_rect().y()) +
271 frame_->visible_rect().x(); 278 frame_->visible_rect().x();
272 } else { 279 } else {
273 offset = (frame_->stride(media::VideoFrame::kUPlane) * 280 offset = (frame_->stride(media::VideoFrame::kUPlane) *
274 (frame_->visible_rect().y() >> y_shift)) + 281 (frame_->visible_rect().y() >> y_shift)) +
275 (frame_->visible_rect().x() >> 1); 282 (frame_->visible_rect().x() >> 1);
276 } 283 }
277 row_bytes[plane] = static_cast<size_t>(frame_->stride(plane)); 284 row_bytes[plane] = static_cast<size_t>(frame_->stride(plane));
278 planes[plane] = frame_->data(plane) + offset; 285 planes[plane] = frame_->data(plane) + offset;
dshwang 2014/10/02 18:23:52 It exposes VideoFrame's data to caller
279 } 286 }
280 } 287 }
281 if (planes && row_bytes)
282 frame_ = NULL;
dshwang 2014/10/02 18:23:52 Removing frame_ can cause seg fault. So I remove |
283 return true; 288 return true;
284 } 289 }
285 290
286 private: 291 private:
287 scoped_refptr<VideoFrame> frame_; 292 scoped_refptr<VideoFrame> frame_;
288 }; 293 };
289 294
290 SkCanvasVideoRenderer::SkCanvasVideoRenderer() 295 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
291 : generator_(NULL), last_frame_timestamp_(media::kNoTimestamp()) { 296 : last_frame_timestamp_(media::kNoTimestamp()) {
292 last_frame_.setIsVolatile(true); 297 last_frame_.setIsVolatile(true);
293 } 298 }
294 299
295 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} 300 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
296 301
297 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame, 302 void SkCanvasVideoRenderer::Paint(const scoped_refptr<VideoFrame>& video_frame,
298 SkCanvas* canvas, 303 SkCanvas* canvas,
299 const gfx::RectF& dest_rect, 304 const gfx::RectF& dest_rect,
300 uint8 alpha, 305 uint8 alpha,
301 SkXfermode::Mode mode, 306 SkXfermode::Mode mode,
302 VideoRotation video_rotation) { 307 VideoRotation video_rotation) {
303 if (alpha == 0) { 308 if (alpha == 0) {
304 return; 309 return;
305 } 310 }
306 311
307 SkRect dest; 312 SkRect dest;
308 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); 313 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
309 314
310 SkPaint paint; 315 SkPaint paint;
311 paint.setAlpha(alpha); 316 paint.setAlpha(alpha);
312 317
313 // Paint black rectangle if there isn't a frame available or the 318 // Paint black rectangle if there isn't a frame available or the
314 // frame has an unexpected format. 319 // frame has an unexpected format.
315 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) { 320 if (!video_frame.get() || !IsYUVOrNative(video_frame->format())) {
316 canvas->drawRect(dest, paint); 321 canvas->drawRect(dest, paint);
317 canvas->flush(); 322 canvas->flush();
318 return; 323 return;
319 } 324 }
320 325
326 VideoImageGenerator* generator = NULL;
321 // Check if we should convert and update |last_frame_|. 327 // Check if we should convert and update |last_frame_|.
322 if (last_frame_.isNull() || 328 if (last_frame_.isNull() ||
323 video_frame->timestamp() != last_frame_timestamp_) { 329 video_frame->timestamp() != last_frame_timestamp_) {
324 generator_ = new VideoImageGenerator(video_frame); 330 generator = new VideoImageGenerator(video_frame);
325 331 // Note: This takes ownership of new VideoImageGenerator instance.
326 // Note: This takes ownership of |generator_|. 332 if (!SkInstallDiscardablePixelRef(generator, &last_frame_)) {
327 if (!SkInstallDiscardablePixelRef(generator_, &last_frame_)) {
328 NOTREACHED(); 333 NOTREACHED();
329 } 334 }
330 335
331 // TODO(rileya): Perform this rotation on the canvas, rather than allocating 336 // TODO(rileya): Perform this rotation on the canvas, rather than allocating
332 // a new bitmap and copying. 337 // a new bitmap and copying.
333 switch (video_rotation) { 338 switch (video_rotation) {
334 case VIDEO_ROTATION_0: 339 case VIDEO_ROTATION_0:
335 break; 340 break;
336 case VIDEO_ROTATION_90: 341 case VIDEO_ROTATION_90:
337 last_frame_ = SkBitmapOperations::Rotate( 342 last_frame_ = SkBitmapOperations::Rotate(
338 last_frame_, SkBitmapOperations::ROTATION_90_CW); 343 last_frame_, SkBitmapOperations::ROTATION_90_CW);
339 break; 344 break;
340 case VIDEO_ROTATION_180: 345 case VIDEO_ROTATION_180:
341 last_frame_ = SkBitmapOperations::Rotate( 346 last_frame_ = SkBitmapOperations::Rotate(
342 last_frame_, SkBitmapOperations::ROTATION_180_CW); 347 last_frame_, SkBitmapOperations::ROTATION_180_CW);
343 break; 348 break;
344 case VIDEO_ROTATION_270: 349 case VIDEO_ROTATION_270:
345 last_frame_ = SkBitmapOperations::Rotate( 350 last_frame_ = SkBitmapOperations::Rotate(
346 last_frame_, SkBitmapOperations::ROTATION_270_CW); 351 last_frame_, SkBitmapOperations::ROTATION_270_CW);
347 break; 352 break;
348 } 353 }
349 354
350 // We copied the frame into a new bitmap and threw out the old one, so we
351 // no longer have a |generator_| around. This should be removed when the
352 // above TODO is addressed.
353 if (video_rotation != VIDEO_ROTATION_0)
354 generator_ = NULL;
355
356 last_frame_timestamp_ = video_frame->timestamp(); 355 last_frame_timestamp_ = video_frame->timestamp();
357 } else if (generator_) {
358 generator_->set_frame(video_frame);
359 } 356 }
360 357
361 paint.setXfermodeMode(mode); 358 paint.setXfermodeMode(mode);
362 359
363 // Paint using |last_frame_|. 360 // Paint using |last_frame_|.
364 paint.setFilterLevel(SkPaint::kLow_FilterLevel); 361 paint.setFilterLevel(SkPaint::kLow_FilterLevel);
365 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); 362 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
366 canvas->flush(); 363 canvas->flush();
364 // SkCanvas::flush() causes the generator to generate SkImage, so delete
365 // |video_frame| not to be outlived.
366 if (generator)
367 generator->discard_frame();
dshwang 2014/10/09 19:59:57 This line caused use-after-free because SkCanvas::
dshwang 2014/10/10 14:13:55 It's wrong analysis. |generator| is deleted by rep
367 } 368 }
368 369
369 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame, 370 void SkCanvasVideoRenderer::Copy(const scoped_refptr<VideoFrame>& video_frame,
370 SkCanvas* canvas) { 371 SkCanvas* canvas) {
371 Paint(video_frame, 372 Paint(video_frame,
372 canvas, 373 canvas,
373 video_frame->visible_rect(), 374 video_frame->visible_rect(),
374 0xff, 375 0xff,
375 SkXfermode::kSrc_Mode, 376 SkXfermode::kSrc_Mode,
376 media::VIDEO_ROTATION_0); 377 media::VIDEO_ROTATION_0);
377 } 378 }
378 379
379 } // namespace media 380 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/skcanvas_video_renderer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698